summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qasn1element.cpp9
-rw-r--r--src/network/ssl/qasn1element_p.h5
-rw-r--r--src/network/ssl/qssl.cpp1
-rw-r--r--src/network/ssl/qssl.h3
-rw-r--r--src/network/ssl/qsslcertificate.cpp8
-rw-r--r--src/network/ssl/qsslcertificate.h4
-rw-r--r--src/network/ssl/qsslcertificate_openssl.cpp17
-rw-r--r--src/network/ssl/qsslcertificate_qt.cpp25
-rw-r--r--src/network/ssl/qsslconfiguration.cpp50
-rw-r--r--src/network/ssl/qsslconfiguration.h9
-rw-r--r--src/network/ssl/qsslconfiguration_p.h3
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp57
-rw-r--r--src/network/ssl/qsslcontext_openssl_p.h4
-rw-r--r--src/network/ssl/qsslellipticcurve.cpp186
-rw-r--r--src/network/ssl/qsslellipticcurve.h102
-rw-r--r--src/network/ssl/qsslellipticcurve_dummy.cpp73
-rw-r--r--src/network/ssl/qsslellipticcurve_openssl.cpp179
-rw-r--r--src/network/ssl/qsslkey_mac.cpp91
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp60
-rw-r--r--src/network/ssl/qsslkey_p.cpp22
-rw-r--r--src/network/ssl/qsslkey_p.h6
-rw-r--r--src/network/ssl/qsslkey_qt.cpp1
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.cpp291
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.h101
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator_p.h65
-rw-r--r--src/network/ssl/qsslsocket.cpp186
-rw-r--r--src/network/ssl/qsslsocket.h46
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp1443
-rw-r--r--src/network/ssl/qsslsocket_mac_p.h125
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp152
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h25
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp105
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h66
-rw-r--r--src/network/ssl/qsslsocket_p.h12
-rw-r--r--src/network/ssl/qsslsocket_winrt.cpp2
-rw-r--r--src/network/ssl/qsslsocket_winrt_p.h2
-rw-r--r--src/network/ssl/ssl.pri18
37 files changed, 3399 insertions, 155 deletions
diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp
index f3f280d863..62e1bb0bee 100644
--- a/src/network/ssl/qasn1element.cpp
+++ b/src/network/ssl/qasn1element.cpp
@@ -336,10 +336,17 @@ QByteArray QAsn1Element::toObjectName() const
QString QAsn1Element::toString() const
{
- if (mType == PrintableStringType || mType == TeletexStringType)
+ // Detect embedded NULs and reject
+ if (qstrlen(mValue) < uint(mValue.size()))
+ return QString();
+
+ if (mType == PrintableStringType || mType == TeletexStringType
+ || mType == Rfc822NameType || mType == DnsNameType
+ || mType == UniformResourceIdentifierType)
return QString::fromLatin1(mValue, mValue.size());
if (mType == Utf8StringType)
return QString::fromUtf8(mValue, mValue.size());
+
return QString();
}
diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h
index 36a7c90de3..c6c4a75d13 100644
--- a/src/network/ssl/qasn1element_p.h
+++ b/src/network/ssl/qasn1element_p.h
@@ -81,6 +81,11 @@ public:
SequenceType = 0x30,
SetType = 0x31,
+ // GeneralNameTypes
+ Rfc822NameType = 0x81,
+ DnsNameType = 0x82,
+ UniformResourceIdentifierType = 0x86,
+
// context specific
Context0Type = 0xA0,
Context3Type = 0xA3
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 98be348e19..f76674f500 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -65,6 +65,7 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
\value Rsa The RSA algorithm.
\value Dsa The DSA algorithm.
+ \value Ec The Elliptic Curve algorithm
\value Opaque A key that should be treated as a 'black box' by QSslKey.
The opaque key facility allows applications to add support for facilities
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index b0a35075fc..b626b884dc 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -55,7 +55,8 @@ namespace QSsl {
enum KeyAlgorithm {
Opaque,
Rsa,
- Dsa
+ Dsa,
+ Ec
};
enum AlternativeNameEntryType {
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index c34d16a37a..2eef2f20aa 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -105,12 +105,16 @@
\value EmailAddress The email address associated with the certificate
*/
+#include <QtCore/qglobal.h>
#ifndef QT_NO_OPENSSL
#include "qsslsocket_openssl_symbols_p.h"
#endif
#ifdef Q_OS_WINRT
#include "qsslsocket_winrt_p.h"
#endif
+#ifdef QT_SECURETRANSPORT
+#include "qsslsocket_mac_p.h"
+#endif
#include "qssl_p.h"
#include "qsslcertificate.h"
@@ -559,7 +563,11 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E
\since 5.0
*/
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+QList<QSslError> QSslCertificate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName)
+#else
QList<QSslError> QSslCertificate::verify(QList<QSslCertificate> certificateChain, const QString &hostName)
+#endif
{
return QSslSocketBackendPrivate::verify(certificateChain, hostName);
}
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
index 6aa7544815..01c9df4314 100644
--- a/src/network/ssl/qsslcertificate.h
+++ b/src/network/ssl/qsslcertificate.h
@@ -136,7 +136,11 @@ public:
static QList<QSslCertificate> fromData(
const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem);
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName = QString());
+#else
static QList<QSslError> verify(QList<QSslCertificate> certificateChain, const QString &hostName = QString());
+#endif
static bool importPkcs12(QIODevice *device,
QSslKey *key, QSslCertificate *cert,
diff --git a/src/network/ssl/qsslcertificate_openssl.cpp b/src/network/ssl/qsslcertificate_openssl.cpp
index 065f0bfd04..f73a16147a 100644
--- a/src/network/ssl/qsslcertificate_openssl.cpp
+++ b/src/network/ssl/qsslcertificate_openssl.cpp
@@ -249,6 +249,12 @@ QSslKey QSslCertificate::publicKey() const
key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
key.d->algorithm = QSsl::Dsa;
key.d->isNull = false;
+#ifndef OPENSSL_NO_EC
+ } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_EC) {
+ key.d->ec = q_EVP_PKEY_get1_EC_KEY(pkey);
+ key.d->algorithm = QSsl::Ec;
+ key.d->isNull = false;
+#endif
} else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) {
// DH unsupported
} else {
@@ -667,11 +673,7 @@ QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteAr
QByteArray decoded = QByteArray::fromBase64(
QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
const unsigned char *data = (const unsigned char *)decoded.data();
-#else
- unsigned char *data = (unsigned char *)decoded.data();
-#endif
if (X509 *x509 = q_d2i_X509(0, &data, decoded.size())) {
certificates << QSslCertificate_from_X509(x509);
@@ -687,12 +689,7 @@ QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteAr
QList<QSslCertificate> certificates;
QSslSocketPrivate::ensureInitialized();
-
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
- const unsigned char *data = (const unsigned char *)der.data();
-#else
- unsigned char *data = (unsigned char *)der.data();
-#endif
+ const unsigned char *data = (const unsigned char *)der.data();
int size = der.size();
while (size > 0 && (count == -1 || certificates.size() < count)) {
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp
index c560c5af87..62bb6e4ad0 100644
--- a/src/network/ssl/qsslcertificate_qt.cpp
+++ b/src/network/ssl/qsslcertificate_qt.cpp
@@ -49,14 +49,9 @@
#include "qsslcertificateextension_p.h"
#include "qasn1element_p.h"
-QT_BEGIN_NAMESPACE
+#include <QtCore/qdatastream.h>
-enum GeneralNameType
-{
- Rfc822NameType = 0x81,
- DnsNameType = 0x82,
- UniformResourceIdentifierType = 0x86
-};
+QT_BEGIN_NAMESPACE
bool QSslCertificate::operator==(const QSslCertificate &other) const
{
@@ -405,10 +400,10 @@ bool QSslCertificatePrivate::parse(const QByteArray &data)
QDataStream nameStream(sanElem.value());
QAsn1Element nameElem;
while (nameElem.read(nameStream)) {
- if (nameElem.type() == Rfc822NameType) {
- subjectAlternativeNames.insert(QSsl::EmailEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size()));
- } else if (nameElem.type() == DnsNameType) {
- subjectAlternativeNames.insert(QSsl::DnsEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size()));
+ if (nameElem.type() == QAsn1Element::Rfc822NameType) {
+ subjectAlternativeNames.insert(QSsl::EmailEntry, nameElem.toString());
+ } else if (nameElem.type() == QAsn1Element::DnsNameType) {
+ subjectAlternativeNames.insert(QSsl::DnsEntry, nameElem.toString());
}
}
}
@@ -462,10 +457,10 @@ bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertific
return false;
const QString key = QString::fromLatin1(items.at(0).toObjectName());
switch (items.at(1).type()) {
- case Rfc822NameType:
- case DnsNameType:
- case UniformResourceIdentifierType:
- result[key] = QString::fromLatin1(items.at(1).value(), items.at(1).value().size());
+ case QAsn1Element::Rfc822NameType:
+ case QAsn1Element::DnsNameType:
+ case QAsn1Element::UniformResourceIdentifierType:
+ result[key] = items.at(1).toString();
break;
}
}
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 55c9a281ba..715e592595 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -203,6 +203,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 &&
@@ -243,6 +244,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() &&
@@ -698,6 +700,50 @@ int QSslConfiguration::sessionTicketLifeTimeHint() const
}
/*!
+ \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<QSslEllipticCurve> 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<QSslEllipticCurve> &curves)
+{
+ d->ellipticCurves = curves;
+}
+
+/*!
\since 5.3
This function returns the protocol negotiated with the server
@@ -728,7 +774,11 @@ QByteArray QSslConfiguration::nextNegotiatedProtocol() const
\sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
*/
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+void QSslConfiguration::setAllowedNextProtocols(const QList<QByteArray> &protocols)
+#else
void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols)
+#endif
{
d->nextAllowedProtocols = protocols;
}
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 0c4e122912..0f53413cfd 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -62,6 +62,7 @@ template<typename T> 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<QSslEllipticCurve> ellipticCurves() const;
+ void setEllipticCurves(const QVector<QSslEllipticCurve> &curves);
+
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
@@ -131,7 +136,11 @@ public:
NextProtocolNegotiationUnsupported
};
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ void setAllowedNextProtocols(const QList<QByteArray> &protocols);
+#else
void setAllowedNextProtocols(QList<QByteArray> protocols);
+#endif
QList<QByteArray> allowedNextProtocols() const;
QByteArray nextNegotiatedProtocol() const;
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 6880f59c4b..54a4786d9f 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<QSslEllipticCurve> ellipticCurves;
+
QByteArray sslSession;
int sslSessionTicketLifeTimeHint;
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index 18eef2fc60..fc291e9d60 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.
@@ -206,7 +207,7 @@ init_context:
// Initialize ciphers
QByteArray cipherString;
- int first = true;
+ bool first = true;
QList<QSslCipher> ciphers = sslContext->sslConfiguration.ciphers();
if (ciphers.isEmpty())
ciphers = QSslSocketPrivate::defaultCiphers();
@@ -238,7 +239,7 @@ init_context:
//
// See also: QSslSocketBackendPrivate::verify()
if (caCertificate.expiryDate() >= QDateTime::currentDateTime()) {
- q_X509_STORE_add_cert(sslContext->ctx->cert_store, (X509 *)caCertificate.handle());
+ q_X509_STORE_add_cert(q_SSL_CTX_get_cert_store(sslContext->ctx), (X509 *)caCertificate.handle());
}
}
@@ -274,8 +275,12 @@ init_context:
// take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
if (configuration.d->privateKey.algorithm() == QSsl::Rsa)
q_EVP_PKEY_set1_RSA(sslContext->pkey, reinterpret_cast<RSA *>(configuration.d->privateKey.handle()));
- else
+ else if (configuration.d->privateKey.algorithm() == QSsl::Dsa)
q_EVP_PKEY_set1_DSA(sslContext->pkey, reinterpret_cast<DSA *>(configuration.d->privateKey.handle()));
+#ifndef OPENSSL_NO_EC
+ else if (configuration.d->privateKey.algorithm() == QSsl::Ec)
+ q_EVP_PKEY_set1_EC_KEY(sslContext->pkey, reinterpret_cast<EC_KEY *>(configuration.d->privateKey.handle()));
+#endif
}
if (!q_SSL_CTX_use_PrivateKey(sslContext->ctx, sslContext->pkey)) {
@@ -327,17 +332,49 @@ init_context:
q_DH_free(dh);
#ifndef OPENSSL_NO_EC
- // Set temp ECDH params
- EC_KEY *ecdh = 0;
- ecdh = q_EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- q_SSL_CTX_set_tmp_ecdh(sslContext->ctx, ecdh);
- q_EC_KEY_free(ecdh);
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (q_SSLeay() >= 0x10002000L) {
+ q_SSL_CTX_ctrl(sslContext->ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL);
+ } else
+#endif
+ {
+ // Set temp ECDH params
+ EC_KEY *ecdh = 0;
+ ecdh = q_EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ q_SSL_CTX_set_tmp_ecdh(sslContext->ctx, ecdh);
+ q_EC_KEY_free(ecdh);
+ }
#endif // OPENSSL_NO_EC
+ const QVector<QSslEllipticCurve> 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) {
+ // SSL_CTX_ctrl wants a non-const pointer as last argument,
+ // but let's avoid a copy into a temporary array
+ if (!q_SSL_CTX_ctrl(sslContext->ctx,
+ SSL_CTRL_SET_CURVES,
+ qcurves.size(),
+ const_cast<int *>(reinterpret_cast<const int *>(qcurves.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;
}
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
static int next_proto_cb(SSL *, unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
@@ -397,7 +434,7 @@ SSL* QSslContext::createSsl()
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
QList<QByteArray> protocols = sslConfiguration.d->nextAllowedProtocols;
if (!protocols.isEmpty()) {
m_supportedNPNVersions.clear();
diff --git a/src/network/ssl/qsslcontext_openssl_p.h b/src/network/ssl/qsslcontext_openssl_p.h
index 41fe77dcdb..77c5b4e2c6 100644
--- a/src/network/ssl/qsslcontext_openssl_p.h
+++ b/src/network/ssl/qsslcontext_openssl_p.h
@@ -77,7 +77,7 @@ public:
void setSessionASN1(const QByteArray &sessionASN1);
int sessionTicketLifeTimeHint() const;
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
// must be public because we want to use it from an OpenSSL callback
struct NPNContext {
NPNContext() : data(0),
@@ -103,7 +103,7 @@ private:
QSslError::SslError errorCode;
QString errorStr;
QSslConfiguration sslConfiguration;
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
QByteArray m_supportedNPNVersions;
NPNContext m_npnContext;
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
diff --git a/src/network/ssl/qsslellipticcurve.cpp b/src/network/ssl/qsslellipticcurve.cpp
new file mode 100644
index 0000000000..d70975591d
--- /dev/null
+++ b/src/network/ssl/qsslellipticcurve.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** 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 <QDebug>
+#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 QSslEllipticCurve QSslEllipticCurve::fromLongName(const QString &name)
+
+ Returns an QSslEllipticCurve instance representing the named curve \a name.
+ The \a name is a long name for the curve, whose exact spelling 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 longName()
+*/
+
+/*!
+ \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 0000000000..d05ed40872
--- /dev/null
+++ b/src/network/ssl/qsslellipticcurve.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** 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 <QtCore/QtGlobal>
+#include <QtCore/QString>
+#include <QtCore/QMetaType>
+#include <QtCore/QHash>
+
+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() Q_DECL_NOTHROW
+ : id(0)
+ {
+ }
+
+ Q_NETWORK_EXPORT static QSslEllipticCurve fromShortName(const QString &name);
+ Q_NETWORK_EXPORT static QSslEllipticCurve fromLongName(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 Q_DECL_NOTHROW
+ {
+ return id != 0;
+ }
+
+ Q_NETWORK_EXPORT bool isTlsNamedCurve() const Q_DECL_NOTHROW;
+
+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 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 0000000000..1c76b56132
--- /dev/null
+++ b/src/network/ssl/qsslellipticcurve_dummy.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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();
+}
+
+QSslEllipticCurve QSslEllipticCurve::fromLongName(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 0000000000..d2821a1c86
--- /dev/null
+++ b/src/network/ssl/qsslellipticcurve_openssl.cpp
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** 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 <openssl/ssl.h>
+#include <openssl/obj_mac.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+QString QSslEllipticCurve::shortName() const
+{
+ QString result;
+#ifndef OPENSSL_NO_EC
+ if (id != 0)
+ result = QString::fromLatin1(q_OBJ_nid2sn(id));
+#endif
+ return result;
+}
+
+QString QSslEllipticCurve::longName() const
+{
+ QString result;
+#ifndef OPENSSL_NO_EC
+ if (id != 0)
+ 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;
+}
+
+QSslEllipticCurve QSslEllipticCurve::fromLongName(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_ln2nid(curveNameLatin1.data());
+ 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 Q_DECL_NOTHROW
+{
+ const int * const tlsNamedCurveNIDsEnd = tlsNamedCurveNIDs + tlsNamedCurveNIDCount;
+ return std::find(tlsNamedCurveNIDs, tlsNamedCurveNIDsEnd, id) != tlsNamedCurveNIDsEnd;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_mac.cpp b/src/network/ssl/qsslkey_mac.cpp
new file mode 100644
index 0000000000..6e15012dc9
--- /dev/null
+++ b/src/network/ssl/qsslkey_mac.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
+** 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 "qsslkey.h"
+#include "qsslkey_p.h"
+
+#include <CommonCrypto/CommonCrypto.h>
+
+QT_USE_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;
+ };
+ size_t plainLength = 0;
+ QByteArray plain(data.size() + blockSize, 0);
+ CCCryptorStatus status = CCCrypt(
+ ccOp, ccAlgorithm, kCCOptionPKCS7Padding,
+ key.constData(), key.size(),
+ iv.constData(),
+ data.constData(), data.size(),
+ plain.data(), plain.size(), &plainLength);
+ if (status == kCCSuccess)
+ return plain.left(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);
+}
diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp
index e4d30ff229..ee4e8efa7c 100644
--- a/src/network/ssl/qsslkey_openssl.cpp
+++ b/src/network/ssl/qsslkey_openssl.cpp
@@ -70,6 +70,13 @@ void QSslKeyPrivate::clear(bool deep)
q_DSA_free(dsa);
dsa = 0;
}
+#ifndef OPENSSL_NO_EC
+ if (ec) {
+ if (deep)
+ q_EC_KEY_free(ec);
+ ec = 0;
+ }
+#endif
if (opaque) {
if (deep)
q_EVP_PKEY_free(opaque);
@@ -99,6 +106,16 @@ bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey)
return true;
}
+#ifndef OPENSSL_NO_EC
+ else if (pkey->type == EVP_PKEY_EC) {
+ isNull = false;
+ algorithm = QSsl::Ec;
+ type = QSsl::PrivateKey;
+ ec = q_EC_KEY_dup(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
@@ -138,12 +155,20 @@ void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhra
: q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase);
if (rsa && rsa == result)
isNull = false;
- } else {
+ } else if (algorithm == QSsl::Dsa) {
DSA *result = (type == QSsl::PublicKey)
? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase)
: q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase);
if (dsa && dsa == result)
isNull = false;
+#ifndef OPENSSL_NO_EC
+ } else if (algorithm == QSsl::Ec) {
+ EC_KEY *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_EC_PUBKEY(bio, &ec, 0, phrase)
+ : q_PEM_read_bio_ECPrivateKey(bio, &ec, 0, phrase);
+ if (ec && ec == result)
+ isNull = false;
+#endif
}
q_BIO_free(bio);
@@ -154,8 +179,14 @@ int QSslKeyPrivate::length() const
if (isNull || algorithm == QSsl::Opaque)
return -1;
- return (algorithm == QSsl::Rsa)
- ? q_BN_num_bits(rsa->n) : q_BN_num_bits(dsa->p);
+ switch (algorithm) {
+ case QSsl::Rsa: return q_BN_num_bits(rsa->n);
+ case QSsl::Dsa: return q_BN_num_bits(dsa->p);
+#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
@@ -182,7 +213,7 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
fail = true;
}
}
- } else {
+ } else if (algorithm == QSsl::Dsa) {
if (type == QSsl::PublicKey) {
if (!q_PEM_write_bio_DSA_PUBKEY(bio, dsa))
fail = true;
@@ -195,6 +226,23 @@ QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
fail = true;
}
}
+#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,
+ // ### the cipher should be selectable in the API:
+ passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
+ (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
+ fail = true;
+ }
+ }
+#endif
+ } else {
+ fail = true;
}
QByteArray pem;
@@ -216,6 +264,10 @@ Qt::HANDLE QSslKeyPrivate::handle() const
return Qt::HANDLE(rsa);
case QSsl::Dsa:
return Qt::HANDLE(dsa);
+#ifndef OPENSSL_NO_EC
+ case QSsl::Ec:
+ return Qt::HANDLE(ec);
+#endif
default:
return Qt::HANDLE(NULL);
}
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp
index 6162034fd7..350a7f2efc 100644
--- a/src/network/ssl/qsslkey_p.cpp
+++ b/src/network/ssl/qsslkey_p.cpp
@@ -103,7 +103,13 @@ QByteArray QSslKeyPrivate::pemHeader() const
return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----");
else if (algorithm == QSsl::Rsa)
return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----");
- return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----");
+ else if (algorithm == QSsl::Dsa)
+ return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----");
+ else if (algorithm == QSsl::Ec)
+ return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----");
+
+ Q_UNREACHABLE();
+ return QByteArray();
}
/*!
@@ -115,7 +121,13 @@ QByteArray QSslKeyPrivate::pemFooter() const
return QByteArrayLiteral("-----END PUBLIC KEY-----");
else if (algorithm == QSsl::Rsa)
return QByteArrayLiteral("-----END RSA PRIVATE KEY-----");
- return QByteArrayLiteral("-----END DSA PRIVATE KEY-----");
+ else if (algorithm == QSsl::Dsa)
+ return QByteArrayLiteral("-----END DSA PRIVATE KEY-----");
+ else if (algorithm == QSsl::Ec)
+ return QByteArrayLiteral("-----END EC PRIVATE KEY-----");
+
+ Q_UNREACHABLE();
+ return QByteArray();
}
/*!
@@ -141,8 +153,8 @@ QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArr
if (!headers.isEmpty()) {
QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd();
do {
- it--;
- extra += it.key() + ": " + it.value() + '\n';
+ --it;
+ extra += it.key() + ": " + it.value() + '\n';
} while (it != headers.constBegin());
extra += '\n';
}
@@ -438,7 +450,7 @@ QDebug operator<<(QDebug debug, const QSslKey &key)
debug << "QSslKey("
<< (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
<< ", " << (key.algorithm() == QSsl::Opaque ? "OPAQUE" :
- (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA"))
+ (key.algorithm() == QSsl::Rsa ? "RSA" : ((key.algorithm() == QSsl::Dsa) ? "DSA" : "EC")))
<< ", " << key.length()
<< ')';
return debug;
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
index 1254b33dfb..9cea860867 100644
--- a/src/network/ssl/qsslkey_p.h
+++ b/src/network/ssl/qsslkey_p.h
@@ -65,6 +65,9 @@ public:
#ifndef QT_NO_OPENSSL
, rsa(0)
, dsa(0)
+#ifndef OPENSSL_NO_EC
+ , ec(0)
+#endif
#endif
{
clear();
@@ -97,6 +100,9 @@ public:
EVP_PKEY *opaque;
RSA *rsa;
DSA *dsa;
+#ifndef OPENSSL_NO_EC
+ EC_KEY *ec;
+#endif
#else
enum Cipher {
DesCbc,
diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp
index bc1ebc3c08..8ad0ea1746 100644
--- a/src/network/ssl/qsslkey_qt.cpp
+++ b/src/network/ssl/qsslkey_qt.cpp
@@ -43,6 +43,7 @@
#include "qsslkey_p.h"
#include "qasn1element_p.h"
+#include <QtCore/qdatastream.h>
#include <QtCore/qcryptographichash.h>
QT_USE_NAMESPACE
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.cpp b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
new file mode 100644
index 0000000000..3b109c580a
--- /dev/null
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.cpp
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** 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 "qsslpresharedkeyauthenticator.h"
+#include "qsslpresharedkeyauthenticator_p.h"
+
+#include <QSharedData>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+*/
+QSslPreSharedKeyAuthenticatorPrivate::QSslPreSharedKeyAuthenticatorPrivate()
+ : maximumIdentityLength(0),
+ maximumPreSharedKeyLength(0)
+{
+}
+
+/*!
+ \class QSslPreSharedKeyAuthenticator
+
+ \brief The QSslPreSharedKeyAuthenticator class provides authentication data for pre
+ shared keys (PSK) ciphersuites.
+
+ \inmodule QtNetwork
+
+ \reentrant
+
+ \ingroup network
+ \ingroup ssl
+ \ingroup shared
+
+ \since 5.5
+
+ The QSslPreSharedKeyAuthenticator class is used by an SSL socket to provide
+ the required authentication data in a pre shared key (PSK) ciphersuite.
+
+ In a PSK handshake, the client must derive a key, which must match the key
+ set on the server. The exact algorithm of deriving the key depends on the
+ application; however, for this purpose, the server may send an \e{identity
+ hint} to the client. This hint, combined with other information (for
+ instance a passphrase), is then used by the client to construct the shared
+ key.
+
+ The QSslPreSharedKeyAuthenticator provides means to client applications for
+ completing the PSK handshake. The client application needs to connect a
+ slot to the QSslSocket::preSharedKeyAuthenticationRequired() signal:
+
+ \code
+
+ connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired,
+ this, &AuthManager::handlePreSharedKeyAuthentication);
+
+ \endcode
+
+ The signal carries a QSslPreSharedKeyAuthenticator object containing the
+ identity hint the server sent to the client, and which must be filled with the
+ corresponding client identity and the derived key:
+
+ \code
+
+ void AuthManager::handlePreSharedKeyAuthentication(QSslPreSharedKeyAuthenticator *authenticator)
+ {
+ authenticator->setIdentity("My Qt App");
+
+ const QByteArray key = deriveKey(authenticator->identityHint(), passphrase);
+ authenticator->setPreSharedKey(key);
+ }
+
+ \endcode
+
+ \note PSK ciphersuites are supported only when using OpenSSL 1.0.1 (or
+ greater) as the SSL backend.
+
+ \sa QSslSocket
+*/
+
+/*!
+ Constructs a default QSslPreSharedKeyAuthenticator object.
+
+ The identity hint, the identity and the key will be initialized to empty
+ byte arrays; the maximum length for both the identity and the key will be
+ initialized to 0.
+*/
+QSslPreSharedKeyAuthenticator::QSslPreSharedKeyAuthenticator()
+ : d(new QSslPreSharedKeyAuthenticatorPrivate)
+{
+}
+
+/*!
+ Destroys the QSslPreSharedKeyAuthenticator object.
+*/
+QSslPreSharedKeyAuthenticator::~QSslPreSharedKeyAuthenticator()
+{
+}
+
+/*!
+ Constructs a QSslPreSharedKeyAuthenticator object as a copy of \a authenticator.
+
+ \sa operator=()
+*/
+QSslPreSharedKeyAuthenticator::QSslPreSharedKeyAuthenticator(const QSslPreSharedKeyAuthenticator &authenticator)
+ : d(authenticator.d)
+{
+}
+
+/*!
+ Assigns the QSslPreSharedKeyAuthenticator object \a authenticator to this object,
+ and returns a reference to the copy.
+*/
+QSslPreSharedKeyAuthenticator &QSslPreSharedKeyAuthenticator::operator=(const QSslPreSharedKeyAuthenticator &authenticator)
+{
+ d = authenticator.d;
+ return *this;
+}
+
+/*!
+ \fn QSslPreSharedKeyAuthenticator &QSslPreSharedKeyAuthenticator::operator=(QSslPreSharedKeyAuthenticator &&authenticator)
+
+ Move-assigns the the QSslPreSharedKeyAuthenticator object \a authenticator to this
+ object, and returns a reference to the moved instance.
+*/
+
+/*!
+ \fn void QSslPreSharedKeyAuthenticator::swap(QSslPreSharedKeyAuthenticator &authenticator)
+
+ Swaps the QSslPreSharedKeyAuthenticator object \a authenticator with this object.
+ This operation is very fast and never fails.
+*/
+
+/*!
+ Returns the PSK identity hint as provided by the server. The interpretation
+ of this hint is left to the application.
+*/
+QByteArray QSslPreSharedKeyAuthenticator::identityHint() const
+{
+ return d->identityHint;
+}
+
+/*!
+ Sets the PSK client identity (to be advised to the server) to \a identity.
+
+ \note it is possible to set an identity whose length is greater than
+ maximumIdentityLength(); in this case, only the first maximumIdentityLength()
+ bytes will be actually sent to the server.
+
+ \sa identity(), maximumIdentityLength()
+*/
+void QSslPreSharedKeyAuthenticator::setIdentity(const QByteArray &identity)
+{
+ d->identity = identity;
+}
+
+/*!
+ Returns the PSK client identity.
+
+ \sa setIdentity()
+*/
+QByteArray QSslPreSharedKeyAuthenticator::identity() const
+{
+ return d->identity;
+}
+
+
+/*!
+ Returns the maximum length, in bytes, of the PSK client identity.
+
+ \note it is possible to set an identity whose length is greater than
+ maximumIdentityLength(); in this case, only the first maximumIdentityLength()
+ bytes will be actually sent to the server.
+
+ \sa setIdentity()
+*/
+int QSslPreSharedKeyAuthenticator::maximumIdentityLength() const
+{
+ return d->maximumIdentityLength;
+}
+
+
+/*!
+ Sets the pre shared key to \a preSharedKey.
+
+ \note it is possible to set a key whose length is greater than the
+ maximumPreSharedKeyLength(); in this case, only the first
+ maximumPreSharedKeyLength() bytes will be actually sent to the server.
+
+ \sa preSharedKey(), maximumPreSharedKeyLength(), QByteArray::fromHex()
+*/
+void QSslPreSharedKeyAuthenticator::setPreSharedKey(const QByteArray &preSharedKey)
+{
+ d->preSharedKey = preSharedKey;
+}
+
+/*!
+ Returns the pre shared key.
+
+ \sa setPreSharedKey()
+*/
+QByteArray QSslPreSharedKeyAuthenticator::preSharedKey() const
+{
+ return d->preSharedKey;
+}
+
+/*!
+ Returns the maximum length, in bytes, of the pre shared key.
+
+ \note it is possible to set a key whose length is greater than the
+ maximumPreSharedKeyLength(); in this case, only the first
+ maximumPreSharedKeyLength() bytes will be actually sent to the server.
+
+ \sa setPreSharedKey()
+*/
+int QSslPreSharedKeyAuthenticator::maximumPreSharedKeyLength() const
+{
+ return d->maximumPreSharedKeyLength;
+}
+
+/*!
+ \relates QSslPreSharedKeyAuthenticator
+ \since 5.5
+
+ Returns true if the authenticator object \a lhs is equal to \a rhs; false
+ otherwise.
+
+ Two authenticator objects are equal if and only if they have the same
+ identity hint, identity, pre shared key, maximum length for the identity
+ and maximum length for the pre shared key.
+
+ \sa operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
+*/
+bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
+{
+ return ((lhs.d == rhs.d) ||
+ (lhs.d->identityHint == rhs.d->identityHint &&
+ lhs.d->identity == rhs.d->identity &&
+ lhs.d->maximumIdentityLength == rhs.d->maximumIdentityLength &&
+ lhs.d->preSharedKey == rhs.d->preSharedKey &&
+ lhs.d->maximumPreSharedKeyLength == rhs.d->maximumPreSharedKeyLength));
+}
+
+/*!
+ \fn bool operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
+ \relates QSslPreSharedKeyAuthenticator
+ \since 5.5
+
+ Returns true if the authenticator object \a lhs is different than \a rhs;
+ false otherwise.
+
+ \sa operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
+*/
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.h b/src/network/ssl/qsslpresharedkeyauthenticator.h
new file mode 100644
index 0000000000..cf2cd4989a
--- /dev/null
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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 QSSLPRESHAREDKEYAUTHENTICATOR_H
+#define QSSLPRESHAREDKEYAUTHENTICATOR_H
+
+#include <QtCore/QtGlobal>
+#include <QtCore/QString>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QMetaType>
+
+QT_BEGIN_NAMESPACE
+
+class QSslPreSharedKeyAuthenticatorPrivate;
+
+class Q_NETWORK_EXPORT QSslPreSharedKeyAuthenticator
+{
+public:
+ QSslPreSharedKeyAuthenticator();
+ ~QSslPreSharedKeyAuthenticator();
+ QSslPreSharedKeyAuthenticator(const QSslPreSharedKeyAuthenticator &authenticator);
+ QSslPreSharedKeyAuthenticator &operator=(const QSslPreSharedKeyAuthenticator &authenticator);
+
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QSslPreSharedKeyAuthenticator &operator=(QSslPreSharedKeyAuthenticator &&authenticator)
+ { d.swap(authenticator.d); return *this; }
+#endif
+
+ void swap(QSslPreSharedKeyAuthenticator &authenticator)
+ {
+ d.swap(authenticator.d);
+ }
+
+ QByteArray identityHint() const;
+
+ void setIdentity(const QByteArray &identity);
+ QByteArray identity() const;
+ int maximumIdentityLength() const;
+
+ void setPreSharedKey(const QByteArray &preSharedKey);
+ QByteArray preSharedKey() const;
+ int maximumPreSharedKeyLength() const;
+
+private:
+ friend Q_NETWORK_EXPORT bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs);
+ friend class QSslSocketBackendPrivate;
+
+ QSharedDataPointer<QSslPreSharedKeyAuthenticatorPrivate> d;
+};
+
+inline bool operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
+{
+ return !operator==(lhs, rhs);
+}
+
+Q_DECLARE_SHARED(QSslPreSharedKeyAuthenticator)
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator)
+Q_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator*)
+
+#endif // QSSLPRESHAREDKEYAUTHENTICATOR_H
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator_p.h b/src/network/ssl/qsslpresharedkeyauthenticator_p.h
new file mode 100644
index 0000000000..9b933a1b1f
--- /dev/null
+++ b/src/network/ssl/qsslpresharedkeyauthenticator_p.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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 QSSLPRESHAREDKEYAUTHENTICATOR_P_H
+#define QSSLPRESHAREDKEYAUTHENTICATOR_P_H
+
+#include <QSharedData>
+
+QT_BEGIN_NAMESPACE
+
+class QSslPreSharedKeyAuthenticatorPrivate : public QSharedData
+{
+public:
+ QSslPreSharedKeyAuthenticatorPrivate();
+
+ QByteArray identityHint;
+
+ QByteArray identity;
+ int maximumIdentityLength;
+
+ QByteArray preSharedKey;
+ int maximumPreSharedKeyLength;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSSLPRESHAREDKEYAUTHENTICATOR_P_H
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index c0c15fb5b1..5c76f327d0 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -281,6 +281,28 @@
\sa peerVerifyError()
*/
+/*!
+ \fn void QSslSocket::preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
+ \since 5.5
+
+ QSslSocket emits this signal when it negotiates a PSK ciphersuite, and
+ therefore a PSK authentication is then required.
+
+ When using PSK, the client must send to the server a valid identity and a
+ valid pre shared key, in order for the SSL handshake to continue.
+ Applications can provide this information in a slot connected to this
+ signal, by filling in the passed \a authenticator object according to their
+ needs.
+
+ \note Ignoring this signal, or failing to provide the required credentials,
+ will cause the handshake to fail, and therefore the connection to be aborted.
+
+ \note The \a authenticator object is owned by the socket and must not be
+ deleted by the application.
+
+ \sa QSslPreSharedKeyAuthenticator
+*/
+
#include "qssl_p.h"
#include "qsslsocket.h"
#include "qsslcipher.h"
@@ -290,6 +312,9 @@
#ifdef Q_OS_WINRT
#include "qsslsocket_winrt_p.h"
#endif
+#ifdef QT_SECURETRANSPORT
+#include "qsslsocket_mac_p.h"
+#endif
#include "qsslconfiguration_p.h"
#include <QtCore/qdebug.h>
@@ -321,6 +346,7 @@ public:
QMutex mutex;
QList<QSslCipher> supportedCiphers;
+ QVector<QSslEllipticCurve> supportedEllipticCurves;
QExplicitlySharedDataPointer<QSslConfigurationPrivate> config;
};
Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData)
@@ -902,6 +928,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();
@@ -1271,6 +1298,120 @@ QList<QSslCipher> QSslSocket::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<QSslEllipticCurve> 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<QSslEllipticCurve> &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<QSslEllipticCurve> &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<QSslEllipticCurve> 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<QSslEllipticCurve> 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
database. \a path can be explicit, or it can contain wildcards in
@@ -2038,6 +2179,46 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph
/*!
\internal
*/
+QVector<QSslEllipticCurve> QSslSocketPrivate::defaultEllipticCurves()
+{
+ QSslSocketPrivate::ensureInitialized();
+ const QMutexLocker locker(&globalData()->mutex);
+ return globalData()->config->ellipticCurves;
+}
+
+/*!
+ \internal
+*/
+QVector<QSslEllipticCurve> QSslSocketPrivate::supportedEllipticCurves()
+{
+ QSslSocketPrivate::ensureInitialized();
+ const QMutexLocker locker(&globalData()->mutex);
+ return globalData()->supportedEllipticCurves;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setDefaultEllipticCurves(const QVector<QSslEllipticCurve> &curves)
+{
+ const QMutexLocker locker(&globalData()->mutex);
+ globalData()->config.detach();
+ globalData()->config->ellipticCurves = curves;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setDefaultSupportedEllipticCurves(const QVector<QSslEllipticCurve> &curves)
+{
+ const QMutexLocker locker(&globalData()->mutex);
+ globalData()->config.detach();
+ globalData()->supportedEllipticCurves = curves;
+}
+
+/*!
+ \internal
+*/
QList<QSslCertificate> QSslSocketPrivate::defaultCaCertificates()
{
QSslSocketPrivate::ensureInitialized();
@@ -2130,10 +2311,8 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
QMutexLocker locker(&globalData()->mutex);
const QSslConfigurationPrivate *global = globalData()->config.constData();
- if (!global) {
- ptr = 0;
+ if (!global)
return;
- }
ptr->ref.store(1);
ptr->peerCertificate = global->peerCertificate;
@@ -2148,6 +2327,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 1fe12e5989..16ae97e49e 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -51,6 +51,8 @@ class QDir;
class QSslCipher;
class QSslCertificate;
class QSslConfiguration;
+class QSslEllipticCurve;
+class QSslPreSharedKeyAuthenticator;
class QSslSocketPrivate;
class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket
@@ -72,20 +74,20 @@ public:
explicit QSslSocket(QObject *parent = 0);
~QSslSocket();
- void resume(); // to continue after proxy authentication required, SSL errors etc.
+ void resume() Q_DECL_OVERRIDE; // to continue after proxy authentication required, SSL errors etc.
// Autostarting the SSL client handshake.
void connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
void connectToHostEncrypted(const QString &hostName, quint16 port, const QString &sslPeerName, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
bool setSocketDescriptor(qintptr socketDescriptor, SocketState state = ConnectedState,
- OpenMode openMode = ReadWrite);
+ OpenMode openMode = ReadWrite) Q_DECL_OVERRIDE;
using QAbstractSocket::connectToHost;
- void connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
- void disconnectFromHost();
+ void connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol) Q_DECL_OVERRIDE;
+ void disconnectFromHost() Q_DECL_OVERRIDE;
- virtual void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value);
- virtual QVariant socketOption(QAbstractSocket::SocketOption option);
+ virtual void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value) Q_DECL_OVERRIDE;
+ virtual QVariant socketOption(QAbstractSocket::SocketOption option) Q_DECL_OVERRIDE;
SslMode mode() const;
bool isEncrypted() const;
@@ -103,16 +105,16 @@ public:
void setPeerVerifyName(const QString &hostName);
// From QIODevice
- qint64 bytesAvailable() const;
- qint64 bytesToWrite() const;
- bool canReadLine() const;
- void close();
- bool atEnd() const;
+ qint64 bytesAvailable() const Q_DECL_OVERRIDE;
+ qint64 bytesToWrite() const Q_DECL_OVERRIDE;
+ bool canReadLine() const Q_DECL_OVERRIDE;
+ void close() Q_DECL_OVERRIDE;
+ bool atEnd() const Q_DECL_OVERRIDE;
bool flush();
void abort();
// From QAbstractSocket:
- void setReadBufferSize(qint64 size);
+ void setReadBufferSize(qint64 size) Q_DECL_OVERRIDE;
// Similar to QIODevice's:
qint64 encryptedBytesAvailable() const;
@@ -149,6 +151,13 @@ public:
static QList<QSslCipher> defaultCiphers();
static QList<QSslCipher> supportedCiphers();
+ // EC settings.
+ QVector<QSslEllipticCurve> ellipticCurves() const;
+ void setEllipticCurves(const QVector<QSslEllipticCurve> &curves);
+ static void setDefaultEllipticCurves(const QVector<QSslEllipticCurve> &curves);
+ static QVector<QSslEllipticCurve> defaultEllipticCurves();
+ static QVector<QSslEllipticCurve> supportedEllipticCurves();
+
// CA settings.
bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
QRegExp::PatternSyntax syntax = QRegExp::FixedString);
@@ -164,11 +173,11 @@ public:
static QList<QSslCertificate> defaultCaCertificates();
static QList<QSslCertificate> systemCaCertificates();
- bool waitForConnected(int msecs = 30000);
+ bool waitForConnected(int msecs = 30000) Q_DECL_OVERRIDE;
bool waitForEncrypted(int msecs = 30000);
- bool waitForReadyRead(int msecs = 30000);
- bool waitForBytesWritten(int msecs = 30000);
- bool waitForDisconnected(int msecs = 30000);
+ bool waitForReadyRead(int msecs = 30000) Q_DECL_OVERRIDE;
+ bool waitForBytesWritten(int msecs = 30000) Q_DECL_OVERRIDE;
+ bool waitForDisconnected(int msecs = 30000) Q_DECL_OVERRIDE;
QList<QSslError> sslErrors() const;
@@ -191,10 +200,11 @@ Q_SIGNALS:
void sslErrors(const QList<QSslError> &errors);
void modeChanged(QSslSocket::SslMode newMode);
void encryptedBytesWritten(qint64 totalBytes);
+ void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
protected:
- qint64 readData(char *data, qint64 maxlen);
- qint64 writeData(const char *data, qint64 len);
+ qint64 readData(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
+ qint64 writeData(const char *data, qint64 len) Q_DECL_OVERRIDE;
private:
Q_DECLARE_PRIVATE(QSslSocket)
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
new file mode 100644
index 0000000000..08f4c1c6a1
--- /dev/null
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -0,0 +1,1443 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
+** 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 "qsslsocket.h"
+
+#include "qsslsocket_mac_p.h"
+#include "qasn1element_p.h"
+#include "qsslcertificate_p.h"
+#include "qsslcipher_p.h"
+#include "qsslkey_p.h"
+
+#include <QtCore/qmessageauthenticationcode.h>
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qsysinfo.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qdebug.h>
+
+#include <algorithm>
+#include <cstddef>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC_WITH_ARGS(QMutex, qt_securetransport_mutex, (QMutex::Recursive))
+
+//#define QSSLSOCKET_DEBUG
+
+bool QSslSocketPrivate::s_libraryLoaded = false;
+bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
+bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
+
+
+#ifndef Q_OS_IOS // dhparam is not used on iOS. (see the SSLSetDiffieHellmanParams call below)
+static const uint8_t dhparam[] =
+ "\x30\x82\x01\x08\x02\x82\x01\x01\x00\x97\xea\xd0\x46\xf7\xae\xa7\x76\x80"
+ "\x9c\x74\x56\x98\xd8\x56\x97\x2b\x20\x6c\x77\xe2\x82\xbb\xc8\x84\xbe\xe7"
+ "\x63\xaf\xcc\x30\xd0\x67\x97\x7d\x1b\xab\x59\x30\xa9\x13\x67\x21\xd7\xd4"
+ "\x0e\x46\xcf\xe5\x80\xdf\xc9\xb9\xba\x54\x9b\x46\x2f\x3b\x45\xfc\x2f\xaf"
+ "\xad\xc0\x17\x56\xdd\x52\x42\x57\x45\x70\x14\xe5\xbe\x67\xaa\xde\x69\x75"
+ "\x30\x0d\xf9\xa2\xc4\x63\x4d\x7a\x39\xef\x14\x62\x18\x33\x44\xa1\xf9\xc1"
+ "\x52\xd1\xb6\x72\x21\x98\xf8\xab\x16\x1b\x7b\x37\x65\xe3\xc5\x11\x00\xf6"
+ "\x36\x1f\xd8\x5f\xd8\x9f\x43\xa8\xce\x9d\xbf\x5e\xd6\x2d\xfa\x0a\xc2\x01"
+ "\x54\xc2\xd9\x81\x54\x55\xb5\x26\xf8\x88\x37\xf5\xfe\xe0\xef\x4a\x34\x81"
+ "\xdc\x5a\xb3\x71\x46\x27\xe3\xcd\x24\xf6\x1b\xf1\xe2\x0f\xc2\xa1\x39\x53"
+ "\x5b\xc5\x38\x46\x8e\x67\x4c\xd9\xdd\xe4\x37\x06\x03\x16\xf1\x1d\x7a\xba"
+ "\x2d\xc1\xe4\x03\x1a\x58\xe5\x29\x5a\x29\x06\x69\x61\x7a\xd8\xa9\x05\x9f"
+ "\xc1\xa2\x45\x9c\x17\xad\x52\x69\x33\xdc\x18\x8d\x15\xa6\x5e\xcd\x94\xf4"
+ "\x45\xbb\x9f\xc2\x7b\x85\x00\x61\xb0\x1a\xdc\x3c\x86\xaa\x9f\x5c\x04\xb3"
+ "\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02";
+#endif
+
+// No ioErr on iOS. (defined in MacErrors.h on OS X)
+#ifdef Q_OS_IOS
+# define ioErr -36
+#endif
+
+static OSStatus _q_SSLRead(QTcpSocket *plainSocket, char *data, size_t *dataLength)
+{
+ Q_ASSERT(plainSocket);
+ Q_ASSERT(data);
+ Q_ASSERT(dataLength);
+
+ const qint64 bytes = plainSocket->read(data, *dataLength);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "read" << bytes;
+#endif
+ if (bytes < 0) {
+ *dataLength = 0;
+ return ioErr;
+ }
+
+ const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : noErr;
+ *dataLength = bytes;
+
+ return err;
+}
+
+static OSStatus _q_SSLWrite(QTcpSocket *plainSocket, const char *data, size_t *dataLength)
+{
+ Q_ASSERT(plainSocket);
+ Q_ASSERT(data);
+ Q_ASSERT(dataLength);
+
+ const qint64 bytes = plainSocket->write(data, *dataLength);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "write" << bytes;
+#endif
+ if (bytes < 0) {
+ *dataLength = 0;
+ return ioErr;
+ }
+
+ const OSStatus err = (size_t(bytes) < *dataLength) ? errSSLWouldBlock : noErr;
+ *dataLength = bytes;
+
+ return err;
+}
+
+void QSslSocketPrivate::ensureInitialized()
+{
+ const QMutexLocker locker(qt_securetransport_mutex);
+ if (s_loadedCiphersAndCerts)
+ return;
+
+ // We have to set it before setDefaultSupportedCiphers,
+ // since this function can trigger static (global)'s initialization
+ // and as a result - recursive ensureInitialized call
+ // from QSslCertificatePrivate's ctor.
+ s_loadedCiphersAndCerts = true;
+
+ QCFType<SSLContextRef> context(SSLCreateContext(Q_NULLPTR, kSSLClientSide, kSSLStreamType));
+ if (context) {
+ QList<QSslCipher> ciphers;
+ QList<QSslCipher> defaultCiphers;
+
+ size_t numCiphers = 0;
+ // Fails only if any of parameters is null.
+ SSLGetNumberSupportedCiphers(context, &numCiphers);
+ QVector<SSLCipherSuite> cfCiphers(numCiphers);
+ // Fails only if any of parameter is null or number of ciphers is wrong.
+ SSLGetSupportedCiphers(context, cfCiphers.data(), &numCiphers);
+
+ for (size_t i = 0; i < size_t(cfCiphers.size()); ++i) {
+ const QSslCipher ciph(QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(cfCiphers[i]));
+ if (!ciph.isNull()) {
+ ciphers << ciph;
+ if (ciph.usedBits() >= 128)
+ defaultCiphers << ciph;
+ }
+ }
+
+ setDefaultSupportedCiphers(ciphers);
+ setDefaultCiphers(defaultCiphers);
+
+ if (!s_loadRootCertsOnDemand)
+ setDefaultCaCertificates(systemCaCertificates());
+ } else {
+ qWarning() << Q_FUNC_INFO << "SSLCreateContext failed";
+ s_loadedCiphersAndCerts = false;
+ }
+
+}
+
+long QSslSocketPrivate::sslLibraryVersionNumber()
+{
+ return 0;
+}
+
+QString QSslSocketPrivate::sslLibraryVersionString()
+{
+ return QStringLiteral("Secure Transport, ") + QSysInfo::prettyProductName();
+}
+
+long QSslSocketPrivate::sslLibraryBuildVersionNumber()
+{
+ return 0;
+}
+
+QString QSslSocketPrivate::sslLibraryBuildVersionString()
+{
+ return sslLibraryVersionString();
+}
+
+bool QSslSocketPrivate::supportsSsl()
+{
+ return true;
+}
+
+void QSslSocketPrivate::resetDefaultCiphers()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QSslSocketPrivate::resetDefaultEllipticCurves()
+{
+ // No public API for this (?).
+ Q_UNIMPLEMENTED();
+}
+
+
+QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
+{
+ QList<QSslCertificate> systemCerts;
+#ifdef Q_OS_OSX
+ // SecTrustSettingsCopyCertificates is not defined on iOS.
+ QCFType<CFArrayRef> cfCerts;
+ OSStatus status = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts);
+ if (status == noErr) {
+ const CFIndex size = CFArrayGetCount(cfCerts);
+ for (CFIndex i = 0; i < size; ++i) {
+ SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
+ QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
+ systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ }
+ } else {
+ // no detailed error handling here
+ qWarning("could not retrieve system CA certificates");
+ }
+#endif
+ return systemCerts;
+}
+
+QSslSocketBackendPrivate::QSslSocketBackendPrivate()
+ : context(Q_NULLPTR)
+{
+}
+
+QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
+{
+ destroySslContext();
+}
+
+void QSslSocketBackendPrivate::continueHandshake()
+{
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "connection encrypted";
+#endif
+ Q_Q(QSslSocket);
+ connectionEncrypted = true;
+ emit q->encrypted();
+ if (autoStartHandshake && pendingClose) {
+ pendingClose = false;
+ q->disconnectFromHost();
+ }
+}
+
+void QSslSocketBackendPrivate::disconnected()
+{
+ if (plainSocket->bytesAvailable() <= 0)
+ destroySslContext();
+ // If there is still buffered data in the plain socket, don't destroy the ssl context yet.
+ // It will be destroyed when the socket is deleted.
+}
+
+void QSslSocketBackendPrivate::disconnectFromHost()
+{
+ if (context) {
+ if (!shutdown) {
+ SSLClose(context);
+ shutdown = true;
+ }
+ }
+ plainSocket->disconnectFromHost();
+}
+
+QSslCipher QSslSocketBackendPrivate::sessionCipher() const
+{
+ SSLCipherSuite cipher = 0;
+ if (context && SSLGetNegotiatedCipher(context, &cipher) == noErr)
+ return QSslCipher_from_SSLCipherSuite(cipher);
+
+ return QSslCipher();
+}
+
+QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const
+{
+ if (!context)
+ return QSsl::UnknownProtocol;
+
+ SSLProtocol protocol = kSSLProtocolUnknown;
+ const OSStatus err = SSLGetNegotiatedProtocolVersion(context, &protocol);
+ if (err != noErr) {
+ qWarning() << Q_FUNC_INFO << "SSLGetNegotiatedProtocolVersion failed:"
+ << int(err);
+ return QSsl::UnknownProtocol;
+ }
+
+ switch (protocol) {
+ case kSSLProtocol2:
+ return QSsl::SslV2;
+ case kSSLProtocol3:
+ return QSsl::SslV3;
+ case kTLSProtocol1:
+ return QSsl::TlsV1_0;
+ case kTLSProtocol11:
+ return QSsl::TlsV1_1;
+ case kTLSProtocol12:
+ return QSsl::TlsV1_2;
+ default:
+ return QSsl::UnknownProtocol;
+ }
+}
+
+void QSslSocketBackendPrivate::startClientEncryption()
+{
+ if (!initSslContext()) {
+ // Error description/code were set, 'error' emitted
+ // by initSslContext, but OpenSSL socket also sets error
+ // emits a signal twice, so ...
+ setError("Unable to init SSL Context", QAbstractSocket::SslInternalError);
+ return;
+ }
+
+ startHandshake();
+}
+
+void QSslSocketBackendPrivate::startServerEncryption()
+{
+ if (!initSslContext()) {
+ // Error description/code were set, 'error' emitted
+ // by initSslContext, but OpenSSL socket also sets error
+ // emits a signal twice, so ...
+ setError("Unable to init SSL Context", QAbstractSocket::SslInternalError);
+ return;
+ }
+
+ startHandshake();
+}
+
+void QSslSocketBackendPrivate::transmit()
+{
+ Q_Q(QSslSocket);
+
+ // If we don't have any SSL context, don't bother transmitting.
+ // Edit: if SSL session closed, don't bother either.
+ if (!context || shutdown)
+ return;
+
+ if (!connectionEncrypted)
+ startHandshake();
+
+ if (connectionEncrypted && !writeBuffer.isEmpty()) {
+ qint64 totalBytesWritten = 0;
+ while (writeBuffer.nextDataBlockSize() > 0) {
+ const size_t nextDataBlockSize = writeBuffer.nextDataBlockSize();
+ size_t writtenBytes = 0;
+ const OSStatus err = SSLWrite(context, writeBuffer.readPointer(), nextDataBlockSize, &writtenBytes);
+ if (err != noErr && err != errSSLWouldBlock) {
+ qWarning() << Q_FUNC_INFO << "SSL write failed with error:" << int(err);
+ setError("SSL write failed", QAbstractSocket::SslInternalError);
+ break;
+ }
+
+ if (writtenBytes) {
+ writeBuffer.free(writtenBytes);
+ totalBytesWritten += writtenBytes;
+ }
+
+ if (writtenBytes < nextDataBlockSize)
+ break;
+ }
+
+ if (totalBytesWritten > 0) {
+ // Don't emit bytesWritten() recursively.
+ if (!emittedBytesWritten) {
+ emittedBytesWritten = true;
+ emit q->bytesWritten(totalBytesWritten);
+ emittedBytesWritten = false;
+ }
+ }
+ }
+
+ if (connectionEncrypted) {
+ QVarLengthArray<char, 4096> data;
+ while (plainSocket->bytesAvailable() > 0) {
+ size_t readBytes = 0;
+ data.resize(4096);
+ if (shutdown) {
+ // SSLRead(context, data.data(), data.size(), &readBytes) fails with errSSLClosedGraceful
+ // if the session was closed (see disconnectFromHost).
+ // SSLClose SSLRead fails and we'll stay in this loop forever.
+ // At the moment we're never here (see the test '!context || shutdown' above) -
+ // we read nothing from the socket as soon as SSL session closed.
+ qCritical() << Q_FUNC_INFO << "read attempt after SSL session closed";
+ size_t nBytes = plainSocket->bytesAvailable();
+ _q_SSLRead(plainSocket, data.data(), &nBytes);
+ } else {
+ const OSStatus err = SSLRead(context, data.data(), data.size(), &readBytes);
+ if (err != noErr && err != errSSLWouldBlock) {
+ qWarning() << Q_FUNC_INFO << "SSLRead failed with:" << int(err);
+ setError("SSL read failed", QAbstractSocket::SslInternalError);
+ break;
+ }
+ }
+
+ if (readBytes) {
+ char *const ptr = buffer.reserve(readBytes);
+ std::copy(data.data(), data.data() + readBytes, ptr);
+ if (readyReadEmittedPointer)
+ *readyReadEmittedPointer = true;
+ emit q->readyRead();
+ }
+ }
+ }
+}
+
+
+QList<QSslError> (QSslSocketBackendPrivate::verify)(QList<QSslCertificate> certificateChain, const QString &hostName)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNUSED(certificateChain)
+ Q_UNUSED(hostName)
+
+ QList<QSslError> errors;
+ errors << QSslError(QSslError::UnspecifiedError);
+
+ return errors;
+}
+
+bool QSslSocketBackendPrivate::importPkcs12(QIODevice *device,
+ QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNUSED(device)
+ Q_UNUSED(key)
+ Q_UNUSED(cert)
+ Q_UNUSED(caCertificates)
+ Q_UNUSED(passPhrase)
+ return false;
+}
+
+QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher)
+{
+ QSslCipher ciph;
+ switch (cipher) {
+ case SSL_RSA_WITH_NULL_MD5:
+ ciph.d->name = QLatin1String("NULL-MD5");
+ ciph.d->protocol = QSsl::SslV3;
+ break;
+ case SSL_RSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("NULL-SHA");
+ ciph.d->protocol = QSsl::SslV3;
+ break;
+ case SSL_RSA_WITH_RC4_128_MD5:
+ ciph.d->name = QLatin1String("RC4-MD5");
+ ciph.d->protocol = QSsl::SslV3;
+ break;
+ case SSL_RSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("RC4-SHA");
+ ciph.d->protocol = QSsl::SslV3;
+ break;
+
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("DES-CBC3-SHA");
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("AES128-SHA");
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("AES128-SHA256");
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("AES256-SHA");
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ ciph.d->name = QLatin1String("AES256-SHA256");
+ break;
+
+ case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("DHE-RSA-DES-CBC3-SHA");
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA");
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("DHE-RSA-AES128-SHA256");
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("DHE-RSA-AES256-SHA");
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ ciph.d->name = QLatin1String("DHE-RSA-AES256-SHA256");
+ break;
+
+ case TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-NULL-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-RC4-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-DES-CBC3-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-AES128-SHA256");
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA");
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ ciph.d->name = QLatin1String("ECDH-ECDSA-AES256-SHA384");
+ break;
+
+ case TLS_ECDH_RSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-NULL-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-DES-CBC3-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES128-SHA256");
+ break;
+ case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA");
+ break;
+ case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ ciph.d->name = QLatin1String("ECDH-RSA-AES256-SHA384");
+ break;
+
+ case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-NULL-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-RC4-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-DES-CBC3-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-AES128-SHA256");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA");
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ ciph.d->name = QLatin1String("ECDHE-ECDSA-AES256-SHA384");
+ break;
+
+ case TLS_ECDHE_RSA_WITH_NULL_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-NULL-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-DES-CBC3-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES128-SHA256");
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA");
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ ciph.d->name = QLatin1String("ECDHE-RSA-AES256-SHA384");
+ break;
+ default:
+ return ciph;
+ }
+ ciph.d->isNull = false;
+
+ // protocol
+ if (ciph.d->protocol == QSsl::SslV3) {
+ ciph.d->protocolString = QLatin1String("SSLv3");
+ } else {
+ ciph.d->protocol = QSsl::TlsV1_2;
+ ciph.d->protocolString = QLatin1String("TLSv1.2");
+ }
+
+ const QStringList bits = ciph.d->name.split('-');
+ if (bits.size() >= 2) {
+ if (bits.size() == 2 || bits.size() == 3) {
+ ciph.d->keyExchangeMethod = QLatin1String("RSA");
+ } else if (ciph.d->name.startsWith("DH-") || ciph.d->name.startsWith("DHE-")) {
+ ciph.d->keyExchangeMethod = QLatin1String("DH");
+ } else if (ciph.d->name.startsWith("ECDH-") || ciph.d->name.startsWith("ECDHE-")) {
+ ciph.d->keyExchangeMethod = QLatin1String("ECDH");
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unknown Kx" << ciph.d->name;
+ }
+
+ if (bits.size() == 2 || bits.size() == 3) {
+ ciph.d->authenticationMethod = QLatin1String("RSA");
+ } else if (ciph.d->name.contains("-ECDSA-")) {
+ ciph.d->authenticationMethod = QLatin1String("ECDSA");
+ } else if (ciph.d->name.contains("-RSA-")) {
+ ciph.d->authenticationMethod = QLatin1String("RSA");
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unknown Au" << ciph.d->name;
+ }
+
+ if (ciph.d->name.contains("RC4-")) {
+ ciph.d->encryptionMethod = QLatin1String("RC4(128)");
+ ciph.d->bits = 128;
+ ciph.d->supportedBits = 128;
+ } else if (ciph.d->name.contains("DES-CBC3-")) {
+ ciph.d->encryptionMethod = QLatin1String("3DES(168)");
+ ciph.d->bits = 168;
+ ciph.d->supportedBits = 168;
+ } else if (ciph.d->name.contains("AES128-")) {
+ ciph.d->encryptionMethod = QLatin1String("AES(128)");
+ ciph.d->bits = 128;
+ ciph.d->supportedBits = 128;
+ } else if (ciph.d->name.contains("AES256-")) {
+ ciph.d->encryptionMethod = QLatin1String("AES(256)");
+ ciph.d->bits = 256;
+ ciph.d->supportedBits = 256;
+ } else if (ciph.d->name.contains("NULL-")) {
+ ciph.d->encryptionMethod = QLatin1String("NULL");
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unknown Enc" << ciph.d->name;
+ }
+ }
+ return ciph;
+}
+
+bool QSslSocketBackendPrivate::initSslContext()
+{
+ Q_Q(QSslSocket);
+
+ Q_ASSERT_X(!context, Q_FUNC_INFO, "invalid socket state, context is not null");
+ Q_ASSERT(plainSocket);
+
+ SSLProtocolSide side = kSSLClientSide;
+ if (mode == QSslSocket::SslServerMode)
+ side = kSSLServerSide;
+
+ context = SSLCreateContext(Q_NULLPTR, side, kSSLStreamType);
+ if (!context) {
+ qWarning() << Q_FUNC_INFO << "SSLCreateContext failed";
+ setError("SSLCreateContext failed", QAbstractSocket::SslInternalError);
+ return false;
+ }
+
+ const OSStatus err = SSLSetIOFuncs(context, reinterpret_cast<SSLReadFunc>(&_q_SSLRead),
+ reinterpret_cast<SSLWriteFunc>(&_q_SSLWrite));
+ if (err != noErr) {
+ qWarning() << Q_FUNC_INFO << "SSLSetIOFuncs failed with error " << int(err);
+ destroySslContext();
+ setError("SSLSetIOFuncs failed", QAbstractSocket::SslInternalError);
+ return false;
+ }
+
+ SSLSetConnection(context, plainSocket);
+
+ if (mode == QSslSocket::SslServerMode
+ && !configuration.localCertificateChain.isEmpty()) {
+ QString errorDescription;
+ QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError;
+ if (!setSessionCertificate(errorDescription, errorCode)) {
+ destroySslContext();
+ setError(errorDescription, errorCode);
+ return false;
+ }
+ }
+
+ if (!setSessionProtocol()) {
+ qWarning() << Q_FUNC_INFO << "failed to set protocol version";
+ destroySslContext();
+ setError("Failed to set protocol version", QAbstractSocket::SslInternalError);
+ return false;
+ }
+
+ if (mode == QSslSocket::SslClientMode) {
+ // enable Server Name Indication (SNI)
+ QString tlsHostName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
+ if (tlsHostName.isEmpty())
+ tlsHostName = hostName;
+
+ const QByteArray ace(QUrl::toAce(tlsHostName));
+ SSLSetPeerDomainName(context, ace.data(), ace.size());
+ // tell SecureTransport we handle peer verification ourselves
+ OSStatus err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
+ if (err == noErr)
+ err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnCertRequested, true);
+
+ if (err != noErr) {
+ qWarning() << Q_FUNC_INFO << "SSLSetSessionOption failed:"<<int(err);
+ destroySslContext();
+ setError(QStringLiteral("SSLSetSessionOption failed: %1").arg(err),
+ QSslSocket::SslInternalError);
+ return false;
+ }
+ //
+ } else {
+ if (configuration.peerVerifyMode != QSslSocket::VerifyNone) {
+// OSStatus err = SSLSetClientSideAuthenticate(context, kAlwaysAuthenticate);
+ OSStatus err = SSLSetClientSideAuthenticate(context, kTryAuthenticate);
+ if (err == noErr) {
+ // We'd like to verify peer ourselves, otherwise handshake will
+ // most probably fail before we can do anything.
+ err = SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
+ }
+
+ if (err != noErr) {
+ qWarning() << Q_FUNC_INFO << "failed to set SSL context option in server mode";
+ destroySslContext();
+ setError(QStringLiteral("failed to set SSL context option in server mode: %1").arg(err),
+ QAbstractSocket::SslInternalError);
+ return false;
+ }
+ }
+#ifndef Q_OS_IOS
+ // No SSLSetDiffieHellmanParams on iOS; calling it is optional according to docs.
+ SSLSetDiffieHellmanParams(context, dhparam, sizeof(dhparam));
+#endif
+ }
+ return true;
+}
+
+void QSslSocketBackendPrivate::destroySslContext()
+{
+ context = Q_NULLPTR;
+}
+
+static QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase);
+
+
+bool QSslSocketBackendPrivate::setSessionCertificate(QString &errorDescription, QAbstractSocket::SocketError &errorCode)
+{
+ Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
+
+ QSslCertificate localCertificate;
+ if (!configuration.localCertificateChain.isEmpty())
+ localCertificate = configuration.localCertificateChain[0];
+
+ if (!localCertificate.isNull()) {
+ // Require a private key as well.
+ if (configuration.privateKey.isNull()) {
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("Cannot provide a certificate with no key");
+ return false;
+ }
+
+ // import certificates and key
+ const QString passPhrase(QString::fromLatin1("foobar"));
+ QCFType<CFDataRef> pkcs12 = _q_makePkcs12(configuration.localCertificateChain,
+ configuration.privateKey, passPhrase).toCFData();
+ QCFType<CFStringRef> password = passPhrase.toCFString();
+ const void *keys[] = { kSecImportExportPassphrase };
+ const void *values[] = { password };
+ QCFType<CFDictionaryRef> options(CFDictionaryCreate(Q_NULLPTR, keys, values, 1,
+ Q_NULLPTR, Q_NULLPTR));
+ CFArrayRef items = Q_NULLPTR;
+ OSStatus err = SecPKCS12Import(pkcs12, options, &items);
+ if (err != noErr) {
+#ifdef QSSLSOCKET_DEBUG
+ qWarning() << Q_FUNC_INFO << plainSocket
+ << QStringLiteral("SecPKCS12Import failed: %1").arg(err);
+#endif
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("SecPKCS12Import failed: %1").arg(err);
+ return false;
+ }
+
+ if (!CFArrayGetCount(items)) {
+#ifdef QSSLSOCKET_DEBUG
+ qWarning() << Q_FUNC_INFO << plainSocket << "SecPKCS12Import returned no items";
+#endif
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("SecPKCS12Import returned no items");
+ return false;
+ }
+
+ CFDictionaryRef import = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0);
+ SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(import, kSecImportItemIdentity);
+ if (!identity) {
+#ifdef QSSLSOCKET_DEBUG
+ qWarning() << Q_FUNC_INFO << plainSocket << "SecPKCS12Import returned no identity";
+#endif
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("SecPKCS12Import returned no identity");
+ return false;
+ }
+
+ QCFType<CFMutableArrayRef> certs = CFArrayCreateMutable(Q_NULLPTR, 0, &kCFTypeArrayCallBacks);
+ if (!certs) {
+ errorCode = QAbstractSocket::SslInternalError;
+ errorDescription = QStringLiteral("Failed to allocate certificates array");
+ return false;
+ }
+
+ CFArrayAppendValue(certs, identity);
+
+ QCFType<CFArrayRef> chain((CFArrayRef)CFDictionaryGetValue(import, kSecImportItemCertChain));
+ if (chain) {
+ for (CFIndex i = 1, e = CFArrayGetCount(chain); i < e; ++i)
+ CFArrayAppendValue(certs, CFArrayGetValueAtIndex(chain, i));
+ }
+
+ err = SSLSetCertificate(context, certs);
+ if (err != noErr) {
+#ifdef QSSLSOCKET_DEBUG
+ qWarning() << Q_FUNC_INFO << plainSocket
+ << QStringLiteral("Cannot set certificate and key: %1").arg(err);
+#endif
+ errorCode = QAbstractSocket::SslInvalidUserDataError;
+ errorDescription = QStringLiteral("Cannot set certificate and key: %1").arg(err);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool QSslSocketBackendPrivate::setSessionProtocol()
+{
+ Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
+
+ OSStatus err = noErr;
+
+ // QSsl::SslV2 == kSSLProtocol2 is disabled in secure transport and
+ // always fails with errSSLIllegalParam:
+ // if (version < MINIMUM_STREAM_VERSION || version > MAXIMUM_STREAM_VERSION)
+ // return errSSLIllegalParam;
+ // where MINIMUM_STREAM_VERSION is SSL_Version_3_0, MAXIMUM_STREAM_VERSION is TLS_Version_1_2.
+ if (configuration.protocol == QSsl::SslV2) {
+ qDebug() << Q_FUNC_INFO << "protocol QSsl::SslV2 is disabled";
+ return false;
+ }
+
+ if (configuration.protocol == QSsl::SslV3) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : SSLv3";
+#endif
+ err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kSSLProtocol3);
+ } else if (configuration.protocol == QSsl::TlsV1_0) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : TLSv1.0";
+#endif
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol1);
+ } else if (configuration.protocol == QSsl::TlsV1_1) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : TLSv1.1";
+#endif
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol11);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol11);
+ } else if (configuration.protocol == QSsl::TlsV1_2) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : TLSv1.2";
+#endif
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol12);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ } else if (configuration.protocol == QSsl::AnyProtocol) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : any";
+#endif
+ // kSSLProtocol3, since kSSLProtocol2 is disabled:
+ err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ } else if (configuration.protocol == QSsl::TlsV1SslV3) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : SSLv3 - TLSv1.2";
+#endif
+ err = SSLSetProtocolVersionMin(context, kSSLProtocol3);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ } else if (configuration.protocol == QSsl::SecureProtocols) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "requesting : TLSv1 - TLSv1.2";
+#endif
+ err = SSLSetProtocolVersionMin(context, kTLSProtocol1);
+ if (err == noErr)
+ err = SSLSetProtocolVersionMax(context, kTLSProtocol12);
+ } else {
+ qDebug() << Q_FUNC_INFO << "no protocol version found in the configuration";
+ return false;
+ }
+
+ return err == noErr;
+}
+
+bool QSslSocketBackendPrivate::verifySessionProtocol() const
+{
+ bool protocolOk = false;
+ if (configuration.protocol == QSsl::AnyProtocol)
+ protocolOk = true;
+ else if (configuration.protocol == QSsl::TlsV1SslV3)
+ protocolOk = (sessionProtocol() >= QSsl::SslV3);
+ else if (configuration.protocol == QSsl::SecureProtocols)
+ protocolOk = (sessionProtocol() >= QSsl::TlsV1_0);
+ else
+ protocolOk = (sessionProtocol() == configuration.protocol);
+
+ return protocolOk;
+}
+
+bool QSslSocketBackendPrivate::verifyPeerTrust()
+{
+ Q_Q(QSslSocket);
+
+ const QSslSocket::PeerVerifyMode verifyMode = configuration.peerVerifyMode;
+ const bool canIgnoreVerify = mode == QSslSocket::SslServerMode
+ && (verifyMode == QSslSocket::QueryPeer
+ || verifyMode == QSslSocket::AutoVerifyPeer
+ || verifyMode == QSslSocket::VerifyNone);
+
+ Q_ASSERT_X(context, Q_FUNC_INFO, "invalid SSL context (null)");
+ Q_ASSERT(plainSocket);
+
+ QCFType<SecTrustRef> trust;
+ OSStatus err = SSLCopyPeerTrust(context, &trust);
+ // !trust - SSLCopyPeerTrust can return noErr but null trust.
+ if (err != noErr || !trust) {
+ if (!canIgnoreVerify) {
+ setError(QStringLiteral("Failed to obtain peer trust: %1").arg(err),
+ QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ QList<QSslError> errors;
+ // store certificates
+ const int certCount = SecTrustGetCertificateCount(trust);
+ // TODO: why this test depends on configuration.peerCertificateChain not being empty????
+ if (configuration.peerCertificateChain.isEmpty()) {
+ // Apple's docs say SetTrustEvaluate must be called before
+ // SecTrustGetCertificateAtIndex, but this results
+ // in 'kSecTrustResultRecoverableTrustFailure', so
+ // here we just ignore 'res' (later we'll use SetAnchor etc.
+ // and evaluate again).
+ SecTrustResultType res = kSecTrustResultInvalid;
+ err = SecTrustEvaluate(trust, &res);
+ if (err != noErr) {
+ // We can not ignore this, it's not even about trust verification
+ // probably ...
+ setError("SecTrustEvaluate failed", QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ return false;
+ }
+
+ for (int i = 0; i < certCount; ++i) {
+ SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
+ QCFType<CFDataRef> derData = SecCertificateCopyData(cert);
+ configuration.peerCertificateChain << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ }
+ }
+
+ if (certCount > 0) {
+ SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0);
+ QCFType<CFDataRef> derData = SecCertificateCopyData(cert);
+ configuration.peerCertificate = QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
+ }
+
+ // check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer)
+ foreach (const QSslCertificate &cert, configuration.peerCertificateChain) {
+ if (QSslCertificatePrivate::isBlacklisted(cert) && !canIgnoreVerify) {
+ const QSslError error(QSslError::CertificateBlacklisted, cert);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
+ const bool doVerifyPeer = verifyMode == QSslSocket::VerifyPeer
+ || (verifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+ // Check the peer certificate itself. First try the subject's common name
+ // (CN) as a wildcard, then try all alternate subject name DNS entries the
+ // same way.
+ if (!configuration.peerCertificate.isNull()) {
+ // but only if we're a client connecting to a server
+ // if we're the server, don't check CN
+ if (mode == QSslSocket::SslClientMode) {
+ const QString peerName(verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName);
+ if (!isMatchingHostname(configuration.peerCertificate, peerName) && !canIgnoreVerify) {
+ // No matches in common names or alternate names.
+ const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ } else {
+ // No peer certificate presented. Report as error if the socket
+ // expected one.
+ if (doVerifyPeer && !canIgnoreVerify) {
+ const QSslError error(QSslError::NoPeerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
+ // TODO: right now we have nothing on server side?
+ if (mode == QSslSocket::SslClientMode) {
+ // verify certificate chain
+ QCFType<CFMutableArrayRef> certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ foreach (const QSslCertificate &cert, configuration.caCertificates) {
+ QCFType<CFDataRef> certData = cert.d->derData.toCFData();
+ QCFType<SecCertificateRef> certRef = SecCertificateCreateWithData(NULL, certData);
+ CFArrayAppendValue(certArray, certRef);
+ }
+ SecTrustSetAnchorCertificates(trust, certArray);
+ SecTrustSetAnchorCertificatesOnly(trust, false);
+
+ SecTrustResultType trustResult = kSecTrustResultInvalid;
+ SecTrustEvaluate(trust, &trustResult);
+ switch (trustResult) {
+ case kSecTrustResultUnspecified:
+ case kSecTrustResultProceed:
+ break;
+ default:
+ if (!canIgnoreVerify) {
+ const QSslError error(QSslError::CertificateUntrusted, configuration.peerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ }
+ }
+ }
+
+ // report errors
+ if (!errors.isEmpty() && !canIgnoreVerify) {
+ sslErrors = errors;
+ if (!checkSslErrors())
+ return false;
+ } else {
+ sslErrors.clear();
+ }
+
+ return true;
+}
+
+/*
+ Copied verbatim from qsslsocket_openssl.cpp
+*/
+bool QSslSocketBackendPrivate::checkSslErrors()
+{
+ Q_Q(QSslSocket);
+ if (sslErrors.isEmpty())
+ return true;
+
+ emit q->sslErrors(sslErrors);
+
+ const bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
+ || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+ const bool doEmitSslError = !verifyErrorsHaveBeenIgnored();
+ // check whether we need to emit an SSL handshake error
+ if (doVerifyPeer && doEmitSslError) {
+ if (q->pauseMode() & QAbstractSocket::PauseOnSslErrors) {
+ pauseSocketNotifiers(q);
+ paused = true;
+ } else {
+ setError(sslErrors.first().errorString(),
+ QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool QSslSocketBackendPrivate::startHandshake()
+{
+ Q_ASSERT(context);
+ Q_Q(QSslSocket);
+
+ OSStatus err = SSLHandshake(context);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << Q_FUNC_INFO << plainSocket << "SSLHandhake returned" << err;
+#endif
+
+ if (err == errSSLWouldBlock) {
+ // startHandshake has to be called again ... later.
+ return false;
+ } else if (err == errSSLServerAuthCompleted) {
+ // TODO: in client mode this happens _before_ ClientCertRequested,
+ // this is the point there we should test the server certificate chain,
+ // not sending any client certificate if the server's certificate validation
+ // fails.
+ // if (!verifyPeerTrust())
+ // ....
+ return startHandshake();
+ } else if (err == errSSLClientCertRequested) {
+ // TODO: If we are here, the server's trust must
+ // be evaluated and accepted already, otherwise,
+ // we can not send our certificate.
+ Q_ASSERT(mode == QSslSocket::SslClientMode);
+ QString errorDescription;
+ QAbstractSocket::SocketError errorCode = QAbstractSocket::UnknownSocketError;
+ // setSessionCertificate does not fail if we have no certificate.
+ // Failure means a real error (invalid certificate, no private key, etc).
+ if (!setSessionCertificate(errorDescription, errorCode)) {
+ qWarning() << Q_FUNC_INFO << "Failed to provide a client certificate";
+ setError(errorDescription, errorCode);
+ return false;
+ } else {
+ // We try to resume a handshake, even if have no
+ // local certificates ... (up to server to deal with our failure).
+ return startHandshake();
+ }
+ } else if (err != errSecSuccess) {
+ setError(QStringLiteral("Error during SSL handshake: %1").arg(err),
+ QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ return false;
+ }
+
+ // Connection aborted during handshake phase.
+ if (q->state() != QAbstractSocket::ConnectedState) {
+ qDebug() << Q_FUNC_INFO << "connection aborted";
+ return false;
+ }
+
+ // check protocol version ourselves, as Secure Transport does not enforce
+ // the requested min / max versions.
+ if (!verifySessionProtocol()) {
+ setError("Protocol version mismatch",
+ QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ return false;
+ }
+
+ if (verifyPeerTrust()) {
+ continueHandshake();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void QSslSocketBackendPrivate::setError(const QString &errorString,
+ QAbstractSocket::SocketError errorCode)
+{
+ Q_Q(QSslSocket);
+
+ q->setErrorString(errorString);
+ q->setSocketError(errorCode);
+ emit q->error(errorCode);
+}
+
+/*
+ PKCS12 helpers.
+*/
+
+static QAsn1Element wrap(quint8 type, const QAsn1Element &child)
+{
+ QByteArray value;
+ QDataStream stream(&value, QIODevice::WriteOnly);
+ child.write(stream);
+ return QAsn1Element(type, value);
+}
+
+static QAsn1Element _q_PKCS7_data(const QByteArray &data)
+{
+ QVector<QAsn1Element> items;
+ items << QAsn1Element::fromObjectId("1.2.840.113549.1.7.1");
+ items << wrap(QAsn1Element::Context0Type,
+ QAsn1Element(QAsn1Element::OctetStringType, data));
+ return QAsn1Element::fromVector(items);
+}
+
+/*!
+ PKCS #12 key derivation.
+
+ Some test vectors:
+ http://www.drh-consultancy.demon.co.uk/test.txt
+*/
+static QByteArray _q_PKCS12_keygen(char id, const QByteArray &salt, const QString &passPhrase, int n, int r)
+{
+ const int u = 20;
+ const int v = 64;
+
+ // password formatting
+ QByteArray passUnicode(passPhrase.size() * 2 + 2, '\0');
+ char *p = passUnicode.data();
+ for (int i = 0; i < passPhrase.size(); ++i) {
+ quint16 ch = passPhrase[i].unicode();
+ *(p++) = (ch & 0xff00) >> 8;
+ *(p++) = (ch & 0xff);
+ }
+
+ // prepare I
+ QByteArray D(64, id);
+ QByteArray S, P;
+ const int sSize = v * ((salt.size() + v - 1) / v);
+ S.resize(sSize);
+ for (int i = 0; i < sSize; ++i) {
+ S[i] = salt[i % salt.size()];
+ }
+ const int pSize = v * ((passUnicode.size() + v - 1) / v);
+ P.resize(pSize);
+ for (int i = 0; i < pSize; ++i) {
+ P[i] = passUnicode[i % passUnicode.size()];
+ }
+ QByteArray I = S + P;
+
+ // apply hashing
+ const int c = (n + u - 1) / u;
+ QByteArray A;
+ QByteArray B;
+ B.resize(v);
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ for (int i = 0; i < c; ++i) {
+ // hash r iterations
+ QByteArray Ai = D + I;
+ for (int j = 0; j < r; ++j) {
+ hash.reset();
+ hash.addData(Ai);
+ Ai = hash.result();
+ }
+
+ for (int j = 0; j < v; ++j) {
+ B[j] = Ai[j % u];
+ }
+
+ // modify I as Ij = (Ij + B + 1) modulo 2^v
+ for (int p = 0; p < I.size(); p += v) {
+ quint8 carry = 1;
+ for (int j = v - 1; j >= 0; --j) {
+ quint16 v = quint8(I[p+j]) + quint8(B[j]) + carry;
+ I[p+j] = v & 0xff;
+ carry = (v & 0xff00) >> 8;
+ }
+ }
+ A += Ai;
+ }
+ return A.left(n);
+}
+
+static QByteArray _q_PKCS12_salt()
+{
+ QByteArray salt;
+ salt.resize(8);
+ for (int i = 0; i < salt.size(); ++i) {
+ salt[i] = (qrand() & 0xff);
+ }
+ return salt;
+}
+
+static QByteArray _q_PKCS12_certBag(const QSslCertificate &cert)
+{
+ QVector<QAsn1Element> items;
+ items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.3");
+
+ // certificate
+ QVector<QAsn1Element> certItems;
+ certItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.22.1");
+ certItems << wrap(QAsn1Element::Context0Type,
+ QAsn1Element(QAsn1Element::OctetStringType, cert.toDer()));
+ items << wrap(QAsn1Element::Context0Type,
+ QAsn1Element::fromVector(certItems));
+
+ // local key id
+ const QByteArray localKeyId = cert.digest(QCryptographicHash::Sha1);
+ QVector<QAsn1Element> idItems;
+ idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21");
+ idItems << wrap(QAsn1Element::SetType,
+ QAsn1Element(QAsn1Element::OctetStringType, localKeyId));
+ items << wrap(QAsn1Element::SetType, QAsn1Element::fromVector(idItems));
+
+ // dump
+ QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items));
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ root.write(stream);
+ return ba;
+}
+
+static QAsn1Element _q_PKCS12_key(const QSslKey &key)
+{
+ Q_ASSERT(key.algorithm() == QSsl::Rsa || key.algorithm() == QSsl::Dsa);
+
+ QVector<QAsn1Element> keyItems;
+ keyItems << QAsn1Element::fromInteger(0);
+ QVector<QAsn1Element> algoItems;
+ if (key.algorithm() == QSsl::Rsa)
+ algoItems << QAsn1Element::fromObjectId(RSA_ENCRYPTION_OID);
+ else if (key.algorithm() == QSsl::Dsa)
+ algoItems << QAsn1Element::fromObjectId(DSA_ENCRYPTION_OID);
+ algoItems << QAsn1Element(QAsn1Element::NullType);
+ keyItems << QAsn1Element::fromVector(algoItems);
+ keyItems << QAsn1Element(QAsn1Element::OctetStringType, key.toDer());
+ return QAsn1Element::fromVector(keyItems);
+}
+
+static QByteArray _q_PKCS12_shroudedKeyBag(const QSslKey &key, const QString &passPhrase, const QByteArray &localKeyId)
+{
+ const int iterations = 2048;
+ QByteArray salt = _q_PKCS12_salt();
+ QByteArray cKey = _q_PKCS12_keygen(1, salt, passPhrase, 24, iterations);
+ QByteArray cIv = _q_PKCS12_keygen(2, salt, passPhrase, 8, iterations);
+
+ // prepare and encrypt data
+ QByteArray plain;
+ QDataStream plainStream(&plain, QIODevice::WriteOnly);
+ _q_PKCS12_key(key).write(plainStream);
+ QByteArray crypted = QSslKeyPrivate::encrypt(QSslKeyPrivate::DesEde3Cbc,
+ plain, cKey, cIv);
+
+ QVector<QAsn1Element> items;
+ items << QAsn1Element::fromObjectId("1.2.840.113549.1.12.10.1.2");
+
+ // key
+ QVector<QAsn1Element> keyItems;
+ QVector<QAsn1Element> algoItems;
+ algoItems << QAsn1Element::fromObjectId("1.2.840.113549.1.12.1.3");
+ QVector<QAsn1Element> paramItems;
+ paramItems << QAsn1Element(QAsn1Element::OctetStringType, salt);
+ paramItems << QAsn1Element::fromInteger(iterations);
+ algoItems << QAsn1Element::fromVector(paramItems);
+ keyItems << QAsn1Element::fromVector(algoItems);
+ keyItems << QAsn1Element(QAsn1Element::OctetStringType, crypted);
+ items << wrap(QAsn1Element::Context0Type,
+ QAsn1Element::fromVector(keyItems));
+
+ // local key id
+ QVector<QAsn1Element> idItems;
+ idItems << QAsn1Element::fromObjectId("1.2.840.113549.1.9.21");
+ idItems << wrap(QAsn1Element::SetType,
+ QAsn1Element(QAsn1Element::OctetStringType, localKeyId));
+ items << wrap(QAsn1Element::SetType,
+ QAsn1Element::fromVector(idItems));
+
+ // dump
+ QAsn1Element root = wrap(QAsn1Element::SequenceType, QAsn1Element::fromVector(items));
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ root.write(stream);
+ return ba;
+}
+
+static QByteArray _q_PKCS12_bag(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase)
+{
+ QVector<QAsn1Element> items;
+
+ // certs
+ for (int i = 0; i < certs.size(); ++i)
+ items << _q_PKCS7_data(_q_PKCS12_certBag(certs[i]));
+
+ // key
+ const QByteArray localKeyId = certs.first().digest(QCryptographicHash::Sha1);
+ items << _q_PKCS7_data(_q_PKCS12_shroudedKeyBag(key, passPhrase, localKeyId));
+
+ // dump
+ QAsn1Element root = QAsn1Element::fromVector(items);
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ root.write(stream);
+ return ba;
+}
+
+static QAsn1Element _q_PKCS12_mac(const QByteArray &data, const QString &passPhrase)
+{
+ const int iterations = 2048;
+
+ // salt generation
+ QByteArray macSalt = _q_PKCS12_salt();
+ QByteArray key = _q_PKCS12_keygen(3, macSalt, passPhrase, 20, iterations);
+
+ // HMAC calculation
+ QMessageAuthenticationCode hmac(QCryptographicHash::Sha1, key);
+ hmac.addData(data);
+
+ QVector<QAsn1Element> algoItems;
+ algoItems << QAsn1Element::fromObjectId("1.3.14.3.2.26");
+ algoItems << QAsn1Element(QAsn1Element::NullType);
+
+ QVector<QAsn1Element> digestItems;
+ digestItems << QAsn1Element::fromVector(algoItems);
+ digestItems << QAsn1Element(QAsn1Element::OctetStringType, hmac.result());
+
+ QVector<QAsn1Element> macItems;
+ macItems << QAsn1Element::fromVector(digestItems);
+ macItems << QAsn1Element(QAsn1Element::OctetStringType, macSalt);
+ macItems << QAsn1Element::fromInteger(iterations);
+ return QAsn1Element::fromVector(macItems);
+}
+
+QByteArray _q_makePkcs12(const QList<QSslCertificate> &certs, const QSslKey &key, const QString &passPhrase)
+{
+ QVector<QAsn1Element> items;
+
+ // version
+ items << QAsn1Element::fromInteger(3);
+
+ // auth safe
+ const QByteArray data = _q_PKCS12_bag(certs, key, passPhrase);
+ items << _q_PKCS7_data(data);
+
+ // HMAC
+ items << _q_PKCS12_mac(data, passPhrase);
+
+ // dump
+ QAsn1Element root = QAsn1Element::fromVector(items);
+ QByteArray ba;
+ QDataStream stream(&ba, QIODevice::WriteOnly);
+ root.write(stream);
+ return ba;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h
new file mode 100644
index 0000000000..d8b7fbd019
--- /dev/null
+++ b/src/network/ssl/qsslsocket_mac_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
+** 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 QSSLSOCKET_MAC_P_H
+#define QSSLSOCKET_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QtNetwork library. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qcore_mac_p.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+
+#include "qabstractsocket.h"
+#include "qsslsocket_p.h"
+
+#include <Security/Security.h>
+#include <Security/SecureTransport.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSslSocketBackendPrivate : public QSslSocketPrivate
+{
+ Q_DECLARE_PUBLIC(QSslSocket)
+public:
+ QSslSocketBackendPrivate();
+ virtual ~QSslSocketBackendPrivate();
+
+ // Final-overriders (QSslSocketPrivate):
+ void continueHandshake() Q_DECL_OVERRIDE;
+ void disconnected() Q_DECL_OVERRIDE;
+ void disconnectFromHost() Q_DECL_OVERRIDE;
+ QSslCipher sessionCipher() const Q_DECL_OVERRIDE;
+ QSsl::SslProtocol sessionProtocol() const Q_DECL_OVERRIDE;
+ void startClientEncryption() Q_DECL_OVERRIDE;
+ void startServerEncryption() Q_DECL_OVERRIDE;
+ void transmit() Q_DECL_OVERRIDE;
+
+ static QList<QSslError> (verify)(QList<QSslCertificate> certificateChain,
+ const QString &hostName);
+
+ static bool importPkcs12(QIODevice *device,
+ QSslKey *key, QSslCertificate *cert,
+ QList<QSslCertificate> *caCertificates,
+ const QByteArray &passPhrase);
+
+ static QSslCipher QSslCipher_from_SSLCipherSuite(SSLCipherSuite cipher);
+
+private:
+ // SSL context management/properties:
+ bool initSslContext();
+ void destroySslContext();
+ bool setSessionCertificate(QString &errorDescription,
+ QAbstractSocket::SocketError &errorCode);
+ bool setSessionProtocol();
+ // Aux. functions to do a verification during handshake phase:
+ bool verifySessionProtocol() const;
+ bool verifyPeerTrust();
+
+ bool checkSslErrors();
+ bool startHandshake();
+
+ // Aux. function, sets:
+ //1) socket error code,
+ //2) error string (description)
+ //3) emits a signal.
+ void setError(const QString &errorString,
+ QAbstractSocket::SocketError errorCode);
+
+ mutable QCFType<SSLContextRef> context;
+
+ Q_DISABLE_COPY(QSslSocketBackendPrivate);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 0e1a3e53c9..83473e8822 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.
@@ -56,6 +57,9 @@
#include "qsslcertificate_p.h"
#include "qsslcipher_p.h"
#include "qsslkey_p.h"
+#include "qsslellipticcurve.h"
+#include "qsslpresharedkeyauthenticator.h"
+#include "qsslpresharedkeyauthenticator_p.h"
#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
@@ -70,6 +74,8 @@
#include <QtCore/qvarlengtharray.h>
#include <QLibrary> // for loading the security lib for the CA store
+#include <string.h>
+
QT_BEGIN_NAMESPACE
#if defined(Q_OS_MACX)
@@ -87,6 +93,10 @@ bool QSslSocketPrivate::s_libraryLoaded = false;
bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+int QSslSocketBackendPrivate::s_indexForSSLExtraData = -1;
+#endif
+
/* \internal
From OpenSSL's thread(3) manual page:
@@ -179,6 +189,18 @@ static unsigned long id_function()
{
return (quintptr)QThread::currentThreadId();
}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
+static unsigned int q_ssl_psk_client_callback(SSL *ssl,
+ const char *hint,
+ char *identity, unsigned int max_identity_len,
+ unsigned char *psk, unsigned int max_psk_len)
+{
+ QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
+ Q_ASSERT(d);
+ return d->tlsPskClientCallback(hint, identity, max_identity_len, psk, max_psk_len);
+}
+#endif
} // extern "C"
QSslSocketBackendPrivate::QSslSocketBackendPrivate()
@@ -344,7 +366,6 @@ bool QSslSocketBackendPrivate::initSslContext()
return false;
}
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
if ((configuration.protocol == QSsl::TlsV1SslV3 ||
configuration.protocol == QSsl::TlsV1_0 ||
configuration.protocol == QSsl::TlsV1_1 ||
@@ -365,7 +386,6 @@ bool QSslSocketBackendPrivate::initSslContext()
qCWarning(lcSsl, "could not set SSL_CTRL_SET_TLSEXT_HOSTNAME, Server Name Indication disabled");
}
}
-#endif
// Clear the session.
errorList.clear();
@@ -388,6 +408,18 @@ bool QSslSocketBackendPrivate::initSslContext()
else
q_SSL_set_accept_state(ssl);
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ // Save a pointer to this object into the SSL structure.
+ if (q_SSLeay() >= 0x10001000L)
+ q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this);
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
+ // Set the client callback for PSK
+ if (q_SSLeay() >= 0x10001000L && mode == QSslSocket::SslClientMode)
+ q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
+#endif
+
return true;
}
@@ -441,6 +473,11 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
q_SSL_load_error_strings();
q_OpenSSL_add_all_algorithms();
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ if (q_SSLeay() >= 0x10001000L)
+ QSslSocketBackendPrivate::s_indexForSSLExtraData = q_SSL_get_ex_new_index(0L, NULL, NULL, NULL, NULL);
+#endif
+
// Initialize OpenSSL's random seed.
if (!q_RAND_status()) {
struct {
@@ -480,6 +517,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
s_loadedCiphersAndCerts = true;
resetDefaultCiphers();
+ resetDefaultEllipticCurves();
#ifndef QT_NO_LIBRARY
//load symbols needed to receive certificates from system store
@@ -608,15 +646,13 @@ void QSslSocketPrivate::resetDefaultCiphers()
STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl);
for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
- if (cipher->valid) {
- QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher);
- if (!ciph.isNull()) {
- // Unconditionally exclude ADH ciphers since they offer no MITM protection
- if (!ciph.name().toLower().startsWith(QLatin1String("adh")))
- ciphers << ciph;
- if (ciph.usedBits() >= 128)
- defaultCiphers << ciph;
- }
+ QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher);
+ if (!ciph.isNull()) {
+ // Unconditionally exclude ADH ciphers since they offer no MITM protection
+ if (!ciph.name().toLower().startsWith(QLatin1String("adh")))
+ ciphers << ciph;
+ if (ciph.usedBits() >= 128)
+ defaultCiphers << ciph;
}
}
}
@@ -628,6 +664,31 @@ void QSslSocketPrivate::resetDefaultCiphers()
setDefaultCiphers(defaultCiphers);
}
+void QSslSocketPrivate::resetDefaultEllipticCurves()
+{
+ QVector<QSslEllipticCurve> curves;
+
+#ifndef OPENSSL_NO_EC
+ const size_t curveCount = q_EC_get_builtin_curves(NULL, 0);
+
+ QVarLengthArray<EC_builtin_curve> builtinCurves(static_cast<int>(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<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
{
ensureInitialized();
@@ -1053,11 +1114,10 @@ bool QSslSocketBackendPrivate::startHandshake()
int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl);
const QList<QPair<int, int> > &lastErrors = _q_sslErrorList()->errors;
+ if (!lastErrors.isEmpty())
+ storePeerCertificates();
for (int i = 0; i < lastErrors.size(); ++i) {
const QPair<int, int> &currentError = lastErrors.at(i);
- // Initialize the peer certificate chain in order to find which certificate caused this error
- if (configuration.peerCertificateChain.isEmpty())
- configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl));
emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.first,
configuration.peerCertificateChain.value(currentError.second)));
if (q->state() != QAbstractSocket::ConnectedState)
@@ -1090,15 +1150,8 @@ bool QSslSocketBackendPrivate::startHandshake()
return false;
}
- // Store the peer certificate and chain. For clients, the peer certificate
- // chain includes the peer certificate; for servers, it doesn't. Both the
- // peer certificate and the chain may be empty if the peer didn't present
- // any certificate.
- if (configuration.peerCertificateChain.isEmpty())
- configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl));
- X509 *x509 = q_SSL_get_peer_certificate(ssl);
- configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
- q_X509_free(x509);
+ // store peer certificate chain
+ storePeerCertificates();
// Start translating errors.
QList<QSslError> errors;
@@ -1206,6 +1259,22 @@ bool QSslSocketBackendPrivate::startHandshake()
return true;
}
+void QSslSocketBackendPrivate::storePeerCertificates()
+{
+ // Store the peer certificate and chain. For clients, the peer certificate
+ // chain includes the peer certificate; for servers, it doesn't. Both the
+ // peer certificate and the chain may be empty if the peer didn't present
+ // any certificate.
+ X509 *x509 = q_SSL_get_peer_certificate(ssl);
+ configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
+ q_X509_free(x509);
+ if (configuration.peerCertificateChain.isEmpty()) {
+ configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl));
+ if (!configuration.peerCertificate.isNull() && mode == QSslSocket::SslServerMode)
+ configuration.peerCertificateChain.prepend(configuration.peerCertificate);
+ }
+}
+
bool QSslSocketBackendPrivate::checkSslErrors()
{
Q_Q(QSslSocket);
@@ -1234,6 +1303,37 @@ bool QSslSocketBackendPrivate::checkSslErrors()
return true;
}
+unsigned int QSslSocketBackendPrivate::tlsPskClientCallback(const char *hint,
+ char *identity, unsigned int max_identity_len,
+ unsigned char *psk, unsigned int max_psk_len)
+{
+ QSslPreSharedKeyAuthenticator authenticator;
+
+ // Fill in some read-only fields (for the user)
+ if (hint)
+ authenticator.d->identityHint = QByteArray::fromRawData(hint, int(::strlen(hint))); // it's NUL terminated, but do not include the NUL
+
+ authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NUL terminated
+ authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
+
+ // Let the client provide the remaining bits...
+ Q_Q(QSslSocket);
+ emit q->preSharedKeyAuthenticationRequired(&authenticator);
+
+ // No PSK set? Return now to make the handshake fail
+ if (authenticator.preSharedKey().isEmpty())
+ return 0;
+
+ // Copy data back into OpenSSL
+ const int identityLength = qMin(authenticator.identity().length(), authenticator.maximumIdentityLength());
+ ::memcpy(identity, authenticator.identity().constData(), identityLength);
+ identity[identityLength] = 0;
+
+ const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength());
+ ::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
+ return pskLength;
+}
+
#ifdef Q_OS_WIN
void QSslSocketBackendPrivate::fetchCaRootForCert(const QSslCertificate &cert)
@@ -1464,10 +1564,8 @@ void QSslSocketBackendPrivate::continueHandshake()
if (readBufferMaxSize)
plainSocket->setReadBufferSize(readBufferMaxSize);
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
if (q_SSL_ctrl((ssl), SSL_CTRL_GET_SESSION_REUSED, 0, NULL))
configuration.peerSessionShared = true;
-#endif
#ifdef QT_DECRYPT_SSL_TRAFFIC
if (ssl->session && ssl->s3) {
@@ -1517,7 +1615,7 @@ void QSslSocketBackendPrivate::continueHandshake()
}
}
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status;
if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) {
@@ -1553,7 +1651,7 @@ QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates
return certificates;
}
-QList<QSslError> QSslSocketBackendPrivate::verify(QList<QSslCertificate> certificateChain, const QString &hostName)
+QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName)
{
QList<QSslError> errors;
if (certificateChain.count() <= 0) {
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index 29907e9ae7..f6887a0339 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -89,9 +89,7 @@
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#include <openssl/crypto.h>
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
#include <openssl/tls1.h>
-#endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
typedef _STACK STACK;
@@ -114,18 +112,23 @@ public:
BIO *writeBio;
SSL_SESSION *session;
QList<QPair<int, int> > errorList;
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ static int s_indexForSSLExtraData; // index used in SSL_get_ex_data to get the matching QSslSocketBackendPrivate
+#endif
// Platform specific functions
- void startClientEncryption();
- void startServerEncryption();
- void transmit();
+ void startClientEncryption() Q_DECL_OVERRIDE;
+ void startServerEncryption() Q_DECL_OVERRIDE;
+ void transmit() Q_DECL_OVERRIDE;
bool startHandshake();
- void disconnectFromHost();
- void disconnected();
- QSslCipher sessionCipher() const;
- QSsl::SslProtocol sessionProtocol() const;
- void continueHandshake();
+ void disconnectFromHost() Q_DECL_OVERRIDE;
+ void disconnected() Q_DECL_OVERRIDE;
+ QSslCipher sessionCipher() const Q_DECL_OVERRIDE;
+ QSsl::SslProtocol sessionProtocol() const Q_DECL_OVERRIDE;
+ void continueHandshake() Q_DECL_OVERRIDE;
bool checkSslErrors();
+ void storePeerCertificates();
+ unsigned int tlsPskClientCallback(const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
#ifdef Q_OS_WIN
void fetchCaRootForCert(const QSslCertificate &cert);
void _q_caRootLoaded(QSslCertificate,QSslCertificate);
@@ -134,7 +137,7 @@ public:
Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher);
static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509);
- static QList<QSslError> verify(QList<QSslCertificate> certificateChain, const QString &hostName);
+ static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName);
static QString getErrorsFromOpenSsl();
static bool importPkcs12(QIODevice *device,
QSslKey *key, QSslCertificate *cert,
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index c1fea930d0..7c07285a06 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -142,17 +142,17 @@ DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return)
DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return 0, return)
+DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return)
+#endif
DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG)
DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG)
DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG)
DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
-#if OPENSSL_VERSION_NUMBER < 0x00908000L
-DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, unsigned char **b, b, long c, c, return 0, return)
-#else // 0.9.8 broke SC and BC by changing this signature.
DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return 0, return)
-#endif
DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return 0, return)
DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, ERR_free_strings, void, DUMMYARG, return, DUMMYARG)
@@ -160,14 +160,22 @@ DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return 0, r
DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return)
DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return)
DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return)
+#endif
DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG)
DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return 0, return)
DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return 0, return)
+#endif
DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return 0, return)
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)
+DEFINEFUNC(int, OBJ_ln2nid, 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)
@@ -178,13 +186,25 @@ DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO
#else
DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+#endif
DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+#endif
#endif
DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC4(EC_KEY *, PEM_read_bio_EC_PUBKEY, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+#endif
DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return)
DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC2(int, PEM_write_bio_EC_PUBKEY, BIO *a, a, EC_KEY *b, b, return 0, return)
+#endif
DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG)
DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return)
DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return 0, return)
@@ -207,12 +227,7 @@ DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return)
DEFINEFUNC3(char *, SSL_CIPHER_description, SSL_CIPHER *a, a, char *b, b, int c, c, return 0, return)
DEFINEFUNC2(int, SSL_CIPHER_get_bits, SSL_CIPHER *a, a, int *b, b, return 0, return)
DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return)
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-// 0.9.8 broke SC and BC by changing this function's signature.
DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return)
-#else
-DEFINEFUNC(int, SSL_CTX_check_private_key, SSL_CTX *a, a, return -1, return)
-#endif
DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return)
DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG)
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
@@ -229,13 +244,9 @@ DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b,
DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return)
DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return)
DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
+DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return 0, return)
DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG)
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-// 0.9.8 broke SC and BC by changing this function's signature.
DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return 0, return)
-#else
-DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, SSL *a, a, return 0, return)
-#endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return)
#else
@@ -254,9 +265,7 @@ DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return)
DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return)
DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG)
DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return 0, return)
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return)
-#endif
DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return)
DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG)
DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG)
@@ -266,6 +275,14 @@ DEFINEFUNC2(int, SSL_set_session, SSL* to, to, SSL_SESSION *session, session, re
DEFINEFUNC(void, SSL_SESSION_free, SSL_SESSION *ses, ses, return, DUMMYARG)
DEFINEFUNC(SSL_SESSION*, SSL_get1_session, SSL *ssl, ssl, return 0, return)
DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return 0, return)
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+DEFINEFUNC5(int, SSL_get_ex_new_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
+DEFINEFUNC3(int, SSL_set_ex_data, SSL *ssl, ssl, int idx, idx, void *arg, arg, return 0, return)
+DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return NULL, return)
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
+DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callback_t callback, callback, return, DUMMYARG)
+#endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#ifndef OPENSSL_NO_SSL2
DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
@@ -350,8 +367,14 @@ DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return 0, r
#ifdef SSLEAY_MACROS
DEFINEFUNC2(int, i2d_DSAPrivateKey, const DSA *a, a, unsigned char **b, b, return -1, return)
DEFINEFUNC2(int, i2d_RSAPrivateKey, const RSA *a, a, unsigned char **b, b, return -1, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC2(int, i2d_ECPrivateKey, const EC_KEY *a, a, unsigned char **b, b, return -1, return)
+#endif
DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return 0, return)
DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return 0, return)
+#ifndef OPENSSL_NO_EC
+DEFINEFUNC3(EC_KEY *, d2i_ECPrivateKey, EC_KEY **a, a, unsigned char **b, b, long c, c, return 0, return)
+#endif
#endif
DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG)
DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG)
@@ -360,7 +383,7 @@ DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return)
DEFINEFUNC(const char *, SSLeay_version, int a, a, return 0, return)
DEFINEFUNC2(int, i2d_SSL_SESSION, SSL_SESSION *in, in, unsigned char **pp, pp, return 0, return)
DEFINEFUNC3(SSL_SESSION *, d2i_SSL_SESSION, SSL_SESSION **a, a, const unsigned char **pp, pp, long length, length, return 0, return)
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
DEFINEFUNC6(int, SSL_select_next_proto, unsigned char **out, out, unsigned char *outlen, outlen,
const unsigned char *in, in, unsigned int inlen, inlen,
const unsigned char *client, client, unsigned int client_len, client_len,
@@ -378,8 +401,13 @@ DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return 0, return)
#ifndef OPENSSL_NO_EC
+DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return 0, return)
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, \
@@ -716,6 +744,10 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(BIO_read)
RESOLVEFUNC(BIO_s_mem)
RESOLVEFUNC(BIO_write)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EC_KEY_get0_group)
+ RESOLVEFUNC(EC_GROUP_get_degree)
+#endif
RESOLVEFUNC(BN_num_bits)
RESOLVEFUNC(CRYPTO_free)
RESOLVEFUNC(CRYPTO_num_locks)
@@ -730,13 +762,21 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(EVP_PKEY_assign)
RESOLVEFUNC(EVP_PKEY_set1_RSA)
RESOLVEFUNC(EVP_PKEY_set1_DSA)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EVP_PKEY_set1_EC_KEY)
+#endif
RESOLVEFUNC(EVP_PKEY_free)
RESOLVEFUNC(EVP_PKEY_get1_DSA)
RESOLVEFUNC(EVP_PKEY_get1_RSA)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EVP_PKEY_get1_EC_KEY)
+#endif
RESOLVEFUNC(EVP_PKEY_new)
RESOLVEFUNC(EVP_PKEY_type)
RESOLVEFUNC(OBJ_nid2sn)
RESOLVEFUNC(OBJ_nid2ln)
+ RESOLVEFUNC(OBJ_sn2nid)
+ RESOLVEFUNC(OBJ_ln2nid)
RESOLVEFUNC(i2t_ASN1_OBJECT)
RESOLVEFUNC(OBJ_obj2txt)
RESOLVEFUNC(OBJ_obj2nid)
@@ -745,13 +785,25 @@ bool q_resolveOpenSslSymbols()
#else
RESOLVEFUNC(PEM_read_bio_DSAPrivateKey)
RESOLVEFUNC(PEM_read_bio_RSAPrivateKey)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(PEM_read_bio_ECPrivateKey)
+#endif
RESOLVEFUNC(PEM_write_bio_DSAPrivateKey)
RESOLVEFUNC(PEM_write_bio_RSAPrivateKey)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(PEM_write_bio_ECPrivateKey)
+#endif
#endif
RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY)
RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(PEM_read_bio_EC_PUBKEY)
+#endif
RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY)
RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY)
+#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(PEM_write_bio_EC_PUBKEY)
+#endif
RESOLVEFUNC(RAND_seed)
RESOLVEFUNC(RAND_status)
RESOLVEFUNC(RSA_new)
@@ -777,6 +829,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_use_PrivateKey)
RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey)
RESOLVEFUNC(SSL_CTX_use_PrivateKey_file)
+ RESOLVEFUNC(SSL_CTX_get_cert_store);
RESOLVEFUNC(SSL_accept)
RESOLVEFUNC(SSL_clear)
RESOLVEFUNC(SSL_connect)
@@ -791,9 +844,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_library_init)
RESOLVEFUNC(SSL_load_error_strings)
RESOLVEFUNC(SSL_new)
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
RESOLVEFUNC(SSL_ctrl)
-#endif
RESOLVEFUNC(SSL_read)
RESOLVEFUNC(SSL_set_accept_state)
RESOLVEFUNC(SSL_set_bio)
@@ -803,6 +854,14 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_SESSION_free)
RESOLVEFUNC(SSL_get1_session)
RESOLVEFUNC(SSL_get_session)
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ RESOLVEFUNC(SSL_get_ex_new_index)
+ RESOLVEFUNC(SSL_set_ex_data)
+ RESOLVEFUNC(SSL_get_ex_data)
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
+ RESOLVEFUNC(SSL_set_psk_client_callback)
+#endif
RESOLVEFUNC(SSL_write)
#ifndef OPENSSL_NO_SSL2
RESOLVEFUNC(SSLv2_client_method)
@@ -880,7 +939,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSLeay_version)
RESOLVEFUNC(i2d_SSL_SESSION)
RESOLVEFUNC(d2i_SSL_SESSION)
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
RESOLVEFUNC(SSL_select_next_proto)
RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb)
RESOLVEFUNC(SSL_get0_next_proto_negotiated)
@@ -889,8 +948,14 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(DH_free)
RESOLVEFUNC(BN_bin2bn)
#ifndef OPENSSL_NO_EC
+ RESOLVEFUNC(EC_KEY_dup)
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 e2e09e4feb..1141ed5125 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -221,18 +221,17 @@ int q_BIO_read(BIO *a, void *b, int c);
BIO_METHOD *q_BIO_s_mem();
int q_BIO_write(BIO *a, const void *b, int c);
int q_BN_num_bits(const BIGNUM *a);
+#ifndef OPENSSL_NO_EC
+const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k);
+int q_EC_GROUP_get_degree(const EC_GROUP* g);
+#endif
int q_CRYPTO_num_locks();
void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int));
void q_CRYPTO_set_id_callback(unsigned long (*a)());
void q_CRYPTO_free(void *a);
DSA *q_DSA_new();
void q_DSA_free(DSA *a);
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-// 0.9.8 broke SC and BC by changing this function's signature.
X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
-#else
-X509 *q_d2i_X509(X509 **a, unsigned char **b, long c);
-#endif
char *q_ERR_error_string(unsigned long a, char *b);
unsigned long q_ERR_get_error();
void q_ERR_free_strings();
@@ -240,14 +239,22 @@ const EVP_CIPHER *q_EVP_des_ede3_cbc();
int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c);
Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b);
int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b);
+#ifndef OPENSSL_NO_EC
+int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b);
+#endif
void q_EVP_PKEY_free(EVP_PKEY *a);
RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a);
DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a);
+#ifndef OPENSSL_NO_EC
+EC_KEY *q_EVP_PKEY_get1_EC_KEY(EVP_PKEY *a);
+#endif
int q_EVP_PKEY_type(int a);
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_OBJ_ln2nid(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);
@@ -259,15 +266,28 @@ void *q_PEM_ASN1_read_bio(d2i_of_void *a, const char *b, BIO *c, void **d, pem_p
#else
DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d);
RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d);
+#ifndef OPENSSL_NO_EC
+EC_KEY *q_PEM_read_bio_ECPrivateKey(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
+#endif
int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d,
int e, pem_password_cb *f, void *g);
int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d,
int e, pem_password_cb *f, void *g);
+#ifndef OPENSSL_NO_EC
+int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+#endif
#endif
DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d);
RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d);
+#ifndef OPENSSL_NO_EC
+EC_KEY *q_PEM_read_bio_EC_PUBKEY(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
+#endif
int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b);
int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b);
+#ifndef OPENSSL_NO_EC
+int q_PEM_write_bio_EC_PUBKEY(BIO *a, EC_KEY *b);
+#endif
void q_RAND_seed(const void *a, int b);
int q_RAND_status();
RSA *q_RSA_new();
@@ -290,12 +310,7 @@ int q_SSL_clear(SSL *a);
char *q_SSL_CIPHER_description(SSL_CIPHER *a, char *b, int c);
int q_SSL_CIPHER_get_bits(SSL_CIPHER *a, int *b);
int q_SSL_connect(SSL *a);
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-// 0.9.8 broke SC and BC by changing this function's signature.
int q_SSL_CTX_check_private_key(const SSL_CTX *a);
-#else
-int q_SSL_CTX_check_private_key(SSL_CTX *a);
-#endif
long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d);
void q_SSL_CTX_free(SSL_CTX *a);
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
@@ -312,13 +327,9 @@ int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c);
int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b);
int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b);
int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c);
+X509_STORE *q_SSL_CTX_get_cert_store(const SSL_CTX *a);
void q_SSL_free(SSL *a);
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-// 0.9.8 broke SC and BC by changing this function's signature.
STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a);
-#else
-STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(SSL *a);
-#endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a);
#else
@@ -328,18 +339,11 @@ int q_SSL_version(const SSL *a);
int q_SSL_get_error(SSL *a, int b);
STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a);
X509 *q_SSL_get_peer_certificate(SSL *a);
-#if OPENSSL_VERSION_NUMBER >= 0x00908000L
-// 0.9.8 broke SC and BC by changing this function's signature.
long q_SSL_get_verify_result(const SSL *a);
-#else
-long q_SSL_get_verify_result(SSL *a);
-#endif
int q_SSL_library_init();
void q_SSL_load_error_strings();
SSL *q_SSL_new(SSL_CTX *a);
-#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg);
-#endif
int q_SSL_read(SSL *a, void *b, int c);
void q_SSL_set_bio(SSL *a, BIO *b, BIO *c);
void q_SSL_set_accept_state(SSL *a);
@@ -349,6 +353,15 @@ int q_SSL_set_session(SSL *to, SSL_SESSION *session);
void q_SSL_SESSION_free(SSL_SESSION *ses);
SSL_SESSION *q_SSL_get1_session(SSL *ssl);
SSL_SESSION *q_SSL_get_session(const SSL *ssl);
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+int q_SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
+int q_SSL_set_ex_data(SSL *ssl, int idx, void *arg);
+void *q_SSL_get_ex_data(const SSL *ssl, int idx);
+#endif
+#ifndef OPENSSL_NO_PSK
+typedef unsigned int (*q_psk_client_callback_t)(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
+void q_SSL_set_psk_client_callback(SSL *ssl, q_psk_client_callback_t callback);
+#endif // OPENSSL_NO_PSK
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
const SSL_METHOD *q_SSLv2_client_method();
const SSL_METHOD *q_SSLv3_client_method();
@@ -432,9 +445,16 @@ BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
#ifndef OPENSSL_NO_EC
// EC Diffie-Hellman support
+EC_KEY *q_EC_KEY_dup(const EC_KEY *src);
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
@@ -490,7 +510,7 @@ const char *q_SSLeay_version(int type);
int q_i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp);
SSL_SESSION *q_d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, long length);
-#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
+#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_NEXTPROTONEG)
int q_SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen,
const unsigned char *client, unsigned int client_len);
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 6e7a2c5520..f0e4896f2b 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -53,6 +53,8 @@
#include "qsslconfiguration_p.h"
#ifndef QT_NO_OPENSSL
#include <private/qsslcontext_openssl_p.h>
+#else
+class QSslContext;
#endif
#include <QtCore/qstringlist.h>
@@ -135,6 +137,12 @@ public:
static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers);
static void resetDefaultCiphers();
+ static QVector<QSslEllipticCurve> defaultEllipticCurves();
+ static QVector<QSslEllipticCurve> supportedEllipticCurves();
+ static void setDefaultEllipticCurves(const QVector<QSslEllipticCurve> &curves);
+ static void setDefaultSupportedEllipticCurves(const QVector<QSslEllipticCurve> &curves);
+ static void resetDefaultEllipticCurves();
+
static QList<QSslCertificate> defaultCaCertificates();
static QList<QSslCertificate> systemCaCertificates();
static void setDefaultCaCertificates(const QList<QSslCertificate> &certs);
@@ -180,8 +188,8 @@ public:
static QList<QByteArray> unixRootCertDirectories(); // used also by QSslContext
- virtual qint64 peek(char *data, qint64 maxSize);
- virtual QByteArray peek(qint64 maxSize);
+ virtual qint64 peek(char *data, qint64 maxSize) Q_DECL_OVERRIDE;
+ virtual QByteArray peek(qint64 maxSize) Q_DECL_OVERRIDE;
// Platform specific functions
virtual void startClientEncryption() = 0;
diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp
index 7527422f75..96bc9b1159 100644
--- a/src/network/ssl/qsslsocket_winrt.cpp
+++ b/src/network/ssl/qsslsocket_winrt.cpp
@@ -654,7 +654,7 @@ HRESULT QSslSocketBackendPrivate::onSslUpgrade(IAsyncAction *action, AsyncStatus
return S_OK;
}
-QList<QSslError> QSslSocketBackendPrivate::verify(QList<QSslCertificate> certificateChain, const QString &hostName)
+QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &certificateChain, const QString &hostName)
{
Q_UNIMPLEMENTED();
Q_UNUSED(certificateChain)
diff --git a/src/network/ssl/qsslsocket_winrt_p.h b/src/network/ssl/qsslsocket_winrt_p.h
index f0df7c6ef1..119635ceba 100644
--- a/src/network/ssl/qsslsocket_winrt_p.h
+++ b/src/network/ssl/qsslsocket_winrt_p.h
@@ -91,7 +91,7 @@ public:
void continueHandshake() Q_DECL_OVERRIDE;
static QList<QSslCipher> defaultCiphers();
- static QList<QSslError> verify(QList<QSslCertificate> certificateChain, const QString &hostName);
+ static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName);
static bool importPkcs12(QIODevice *device,
QSslKey *key, QSslCertificate *cert,
QList<QSslCertificate> *caCertificates,
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index d0ba93b53f..29c47cd7c6 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -9,11 +9,14 @@ 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 \
ssl/qsslsocket.h \
ssl/qsslsocket_p.h \
+ ssl/qsslpresharedkeyauthenticator.h \
+ ssl/qsslpresharedkeyauthenticator_p.h \
ssl/qsslcertificateextension.h \
ssl/qsslcertificateextension_p.h
SOURCES += ssl/qasn1element.cpp \
@@ -21,9 +24,11 @@ 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 \
+ ssl/qsslpresharedkeyauthenticator.cpp \
ssl/qsslcertificateextension.cpp
winrt {
@@ -32,7 +37,17 @@ 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
+ }
+
+ contains(QT_CONFIG, securetransport) {
+ HEADERS += ssl/qsslsocket_mac_p.h
+ SOURCES += ssl/qsslcertificate_qt.cpp \
+ ssl/qsslkey_qt.cpp \
+ ssl/qsslkey_mac.cpp \
+ ssl/qsslsocket_mac.cpp \
+ ssl/qsslellipticcurve_dummy.cpp
}
}
@@ -42,6 +57,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