From 81a7e0ea67e71f4128f9f5b070e1dde28dfc616f Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 27 Apr 2021 14:44:02 +0200 Subject: Document TLS plugin classes (private, internal) Also, a minor clean-up: isMatchingHostname() overload was never used, deleted (and it could not be used safely, since it requires the name to be normalized first). The file (qtlsbackend.cpp) was re-shuffled, to have backend on top of the classes which this backend is factory for. Fixes: QTBUG-91929 Change-Id: I435c69b167f57f7c3f76e34449c52f665dc6f7c2 Reviewed-by: Edward Welbourne (cherry picked from commit c771ad8cdfccb2678664d9e7c54669acf82fedaa) Reviewed-by: Qt Cherry-pick Bot --- src/network/ssl/qtlsbackend.cpp | 1754 ++++++++++++++++++++++++++++++++++++--- src/network/ssl/qtlsbackend_p.h | 43 +- 2 files changed, 1671 insertions(+), 126 deletions(-) (limited to 'src') diff --git a/src/network/ssl/qtlsbackend.cpp b/src/network/ssl/qtlsbackend.cpp index 4c412d436f..9733168aab 100644 --- a/src/network/ssl/qtlsbackend.cpp +++ b/src/network/ssl/qtlsbackend.cpp @@ -146,108 +146,44 @@ private: Q_GLOBAL_STATIC(BackendCollection, backends); -namespace QTlsPrivate { - -TlsKey::~TlsKey() = default; - -QByteArray TlsKey::pemHeader() const -{ - if (type() == QSsl::PublicKey) - return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----"); - else if (algorithm() == QSsl::Rsa) - return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----"); - else if (algorithm() == QSsl::Dsa) - return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----"); - else if (algorithm() == QSsl::Ec) - return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----"); - else if (algorithm() == QSsl::Dh) - return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); - - Q_UNREACHABLE(); - return {}; -} - -QByteArray TlsKey::pemFooter() const -{ - if (type() == QSsl::PublicKey) - return QByteArrayLiteral("-----END PUBLIC KEY-----"); - else if (algorithm() == QSsl::Rsa) - return QByteArrayLiteral("-----END RSA PRIVATE KEY-----"); - else if (algorithm() == QSsl::Dsa) - return QByteArrayLiteral("-----END DSA PRIVATE KEY-----"); - else if (algorithm() == QSsl::Ec) - return QByteArrayLiteral("-----END EC PRIVATE KEY-----"); - else if (algorithm() == QSsl::Dh) - return QByteArrayLiteral("-----END PRIVATE KEY-----"); - - Q_UNREACHABLE(); - return {}; -} - -X509Certificate::~X509Certificate() = default; - -TlsKey *X509Certificate::publicKey() const -{ - // 'no-ssl' build has no key support either. - return nullptr; -} - -#if QT_CONFIG(ssl) - -TlsCryptograph::~TlsCryptograph() = default; - -void TlsCryptograph::checkSettingSslContext(QSharedPointer tlsContext) -{ - Q_UNUSED(tlsContext); -} - -QSharedPointer TlsCryptograph::sslContext() const -{ - return {}; -} - -void TlsCryptograph::enableHandshakeContinuation() -{ -} - -void TlsCryptograph::cancelCAFetch() -{ -} +/*! + \class QTlsBackend + \internal (Network-private) + \brief QTlsBackend is a factory class, providing implementations + for the QSsl classes. -bool TlsCryptograph::hasUndecryptedData() const -{ - return false; -} + The purpose of QTlsBackend is to enable and simplify the addition + of new TLS backends to be used by QSslSocket and related classes. + Starting from Qt 6.1, these backends have plugin-based design (and + thus can co-exist simultaneously, unlike pre 6.1 times), although + any given run of a program can only use one of them. -QList TlsCryptograph::ocsps() const -{ - return {}; -} + Inheriting from QTlsBackend and creating an object of such + a class adds a new backend into the list of available TLS backends. -bool TlsCryptograph::isMatchingHostname(const QSslCertificate &cert, const QString &peerName) -{ - return QSslSocketPrivate::isMatchingHostname(cert, peerName); -} + A new backend must provide a list of classes, features and protocols + it supports, and override the corresponding virtual functions that + create backend-specific implementations for these QSsl-classes. -bool TlsCryptograph::isMatchingHostname(const QString &cn, const QString &hostname) -{ - return QSslSocketPrivate::isMatchingHostname(cn, hostname); -} + The base abstract class - QTlsBackend - provides, where possible, + default implementations of its virtual member functions. These + default implementations can be overridden by a derived backend + class, if needed. -void TlsCryptograph::setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode, - const QString &errorDescription) const -{ - Q_ASSERT(d); - d->setErrorAndEmit(errorCode, errorDescription); -} + QTlsBackend also provides some auxiliary functions that a derived + backend class can use to interact with the internals of network-private classes. -#endif // QT_CONFIG(ssl) + \sa QSslSocket::availableBackends(), supportedFeatures(), supportedProtocols(), implementedClasses() +*/ -#if QT_CONFIG(dtls) -DtlsBase::~DtlsBase() = default; -#endif // QT_CONFIG(dtls) +/*! + \fn QString QTlsBackend::backendName() const + \internal + Returns the name of this backend. The name will be reported by QSslSocket::availableBackends(). + Example of backend names: "openssl", "schannel", "securetransport". -} // namespace QTlsPrivate + \sa QSslSocket::availableBackends(), isValid() +*/ const QString QTlsBackend::builtinBackendNames[] = { QStringLiteral("schannel"), @@ -256,79 +192,181 @@ const QString QTlsBackend::builtinBackendNames[] = { QStringLiteral("cert-only") }; +/*! + \internal + The default constructor, adds a new backend to the list of available backends. + + \sa ~QTlsBackend(), availableBackendNames(), QSslSocket::availableBackends() +*/ QTlsBackend::QTlsBackend() { if (backends()) backends->addBackend(this); } +/*! + \internal + Removes this backend from the list of available backends. + + \sa QTlsBackend(), availableBackendNames(), QSslSocket::availableBackends() +*/ QTlsBackend::~QTlsBackend() { if (backends()) backends->removeBackend(this); } +/*! + \internal + Returns \c true if this backend was initialised successfully. The default implementation + always returns \c true. + + \note This function must be overridden if a particular backend has a non-trivial initialization + that can fail. If reimplemented, returning \c false will exclude this backend from the list of + backends, reported as available by QSslSocket. + + \sa QSslSocket::availableBackends() +*/ + bool QTlsBackend::isValid() const { return true; } +/*! + \internal + Returns an implementations-specific integer value, representing the TLS library's + version, that is currently used by this backend (i.e. runtime library version). + The default implementation returns 0. + + \sa tlsLibraryBuildVersionNumber() +*/ long QTlsBackend::tlsLibraryVersionNumber() const { return 0; } +/*! + \internal + Returns an implementation-specific string, representing the TLS library's version, + that is currently used by this backend (i.e. runtime library version). The default + implementation returns an empty string. + + \sa tlsLibraryBuildVersionString() +*/ + QString QTlsBackend::tlsLibraryVersionString() const { return {}; } +/*! + \internal + Returns an implementation-specific integer value, representing the TLS library's + version that this backend was built against (i.e. compile-time library version). + The default implementation returns 0. + + \sa tlsLibraryVersionNumber() +*/ + long QTlsBackend::tlsLibraryBuildVersionNumber() const { return 0; } +/*! + \internal + Returns an implementation-specific string, representing the TLS library's version + that this backend was built against (i.e. compile-time version). The default + implementation returns an empty string. + + \sa tlsLibraryVersionString() +*/ QString QTlsBackend::tlsLibraryBuildVersionString() const { return {}; } +/*! + \internal + QSslSocket and related classes call this function to ensure that backend's internal + resources - e.g. CA certificates, or ciphersuites - were properly initialized. +*/ void QTlsBackend::ensureInitialized() const { } -QString QTlsBackend::backendName() const -{ - return QStringLiteral("dummyTLS"); -} - #define REPORT_MISSING_SUPPORT(message) \ qCWarning(lcSsl) << "The backend" << backendName() << message +/*! + \internal + If QSsl::ImplementedClass::Key is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance + of an implementation-specific type, inheriting from the class QTlsPrivate::TlsKey. + The default implementation of this function returns \nullptr. + + \sa QSslKey, implementedClasses(), QTlsPrivate::TlsKey +*/ QTlsPrivate::TlsKey *QTlsBackend::createKey() const { REPORT_MISSING_SUPPORT("does not support QSslKey"); return nullptr; } +/*! + \internal + If QSsl::ImplementedClass::Certificate is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance of an + implementation-specific type, inheriting from the class QTlsPrivate::X509Certificate. + The default implementation of this function returns \nullptr. + + \sa QSslCertificate, QTlsPrivate::X509Certificate, implementedClasses() +*/ QTlsPrivate::X509Certificate *QTlsBackend::createCertificate() const { REPORT_MISSING_SUPPORT("does not support QSslCertificate"); return nullptr; } +/*! + \internal + This function returns a list of system CA certificates - e.g. certificates, loaded + from a system store, if available. This function allows implementation of the class + QSslConfiguration. The default implementation of this function returns an empty list. + + \sa QSslCertificate, QSslConfiguration +*/ QList QTlsBackend::systemCaCertificates() const { REPORT_MISSING_SUPPORT("does not provide system CA certificates"); return {}; } +/*! + \internal + If QSsl::ImplementedClass::Socket is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance of an + implementation-specific type, inheriting from the class QTlsPrivate::TlsCryptograph. + The default implementation of this function returns \nullptr. + + \sa QSslSocket, QTlsPrivate::TlsCryptograph, implementedClasses() +*/ QTlsPrivate::TlsCryptograph *QTlsBackend::createTlsCryptograph() const { REPORT_MISSING_SUPPORT("does not support QSslSocket"); return nullptr; } +/*! + \internal + If QSsl::ImplementedClass::Dtls is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance of an + implementation-specific type, inheriting from the class QTlsPrivate::DtlsCryptograph. + The default implementation of this function returns \nullptr. + + \sa QDtls, QTlsPrivate::DtlsCryptograph, implementedClasses() +*/ QTlsPrivate::DtlsCryptograph *QTlsBackend::createDtlsCryptograph(QDtls *qObject, int mode) const { Q_UNUSED(qObject); @@ -337,42 +375,104 @@ QTlsPrivate::DtlsCryptograph *QTlsBackend::createDtlsCryptograph(QDtls *qObject, return nullptr; } +/*! + \internal + If QSsl::ImplementedClass::DtlsCookie is present in this backend's implementedClasses(), + the backend must reimplement this method to return a dynamically-allocated instance of an + implementation-specific type, inheriting from the class QTlsPrivate::DtlsCookieVerifier. The + default implementation returns \nullptr. + + \sa QDtlsClientVerifier, QTlsPrivate::DtlsCookieVerifier, implementedClasses() +*/ QTlsPrivate::DtlsCookieVerifier *QTlsBackend::createDtlsCookieVerifier() const { REPORT_MISSING_SUPPORT("does not support DTLS cookies"); return nullptr; } +/*! + \internal + If QSsl::SupportedFeature::CertificateVerification is present in this backend's + supportedFeatures(), the backend must reimplement this method to return a pointer + to a function, that checks a certificate (or a chain of certificates) against available + CA certificates. The default implementation returns \nullptr. + + \sa supportedFeatures(), QSslCertificate +*/ + QTlsPrivate::X509ChainVerifyPtr QTlsBackend::X509Verifier() const { REPORT_MISSING_SUPPORT("does not support (manual) certificate verification"); return nullptr; } +/*! + \internal + Returns a pointer to function, that reads certificates in PEM format. The + default implementation returns \nullptr. + + \sa QSslCertificate +*/ QTlsPrivate::X509PemReaderPtr QTlsBackend::X509PemReader() const { REPORT_MISSING_SUPPORT("cannot read PEM format"); return nullptr; } +/*! + \internal + Returns a pointer to function, that can read certificates in DER format. + The default implementation returns \nullptr. + + \sa QSslCertificate +*/ QTlsPrivate::X509DerReaderPtr QTlsBackend::X509DerReader() const { REPORT_MISSING_SUPPORT("cannot read DER format"); return nullptr; } +/*! + \internal + Returns a pointer to function, that can read certificates in PKCS 12 format. + The default implementation returns \nullptr. + + \sa QSslCertificate +*/ QTlsPrivate::X509Pkcs12ReaderPtr QTlsBackend::X509Pkcs12Reader() const { REPORT_MISSING_SUPPORT("cannot read PKCS12 format"); return nullptr; } +/*! + \internal + If QSsl::ImplementedClass::EllipticCurve is present in this backend's implementedClasses(), + and the backend provides information about supported curves, it must reimplement this + method to return a list of unique identifiers of the supported elliptic curves. The default + implementation returns an empty list. + + \note The meaning of a curve identifier is implementation-specific. + + \sa implemenedClasses(), QSslEllipticCurve +*/ QList QTlsBackend::ellipticCurvesIds() const { REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve"); return {}; } +/*! + \internal + If this backend provides information about available elliptic curves, this + function should return a unique integer identifier for a curve named \a name, + which is a conventional short name for the curve. The default implementation + returns 0. + + \note The meaning of a curve identifier is implementation-specific. + + \sa QSslEllipticCurve::shortName() +*/ int QTlsBackend::curveIdFromShortName(const QString &name) const { Q_UNUSED(name); @@ -380,6 +480,17 @@ int QTlsBackend::curveIdFromShortName(const QString &name) const return 0; } +/*! + \internal + If this backend provides information about available elliptic curves, this + function should return a unique integer identifier for a curve named \a name, + which is a conventional long name for the curve. The default implementation + returns 0. + + \note The meaning of a curve identifier is implementation-specific. + + \sa QSslElliptiCurve::longName() +*/ int QTlsBackend::curveIdFromLongName(const QString &name) const { Q_UNUSED(name); @@ -387,6 +498,16 @@ int QTlsBackend::curveIdFromLongName(const QString &name) const return 0; } +/*! + \internal + If this backend provides information about available elliptic curves, + this function should return a conventional short name for a curve identified + by \a cid. The default implementation returns an empty string. + + \note The meaning of a curve identifier is implementation-specific. + + \sa ellipticCurvesIds(), QSslEllipticCurve::shortName() +*/ QString QTlsBackend::shortNameForId(int cid) const { Q_UNUSED(cid); @@ -394,6 +515,16 @@ QString QTlsBackend::shortNameForId(int cid) const return {}; } +/*! + \internal + If this backend provides information about available elliptic curves, + this function should return a conventional long name for a curve identified + by \a cid. The default implementation returns an empty string. + + \note The meaning of a curve identifier is implementation-specific. + + \sa ellipticCurvesIds(), QSslEllipticCurve::shortName() +*/ QString QTlsBackend::longNameForId(int cid) const { Q_UNUSED(cid); @@ -401,6 +532,14 @@ QString QTlsBackend::longNameForId(int cid) const return {}; } +/*! + \internal + Returns true if the elliptic curve identified by \a cid is one of the named + curves, that can be used in the key exchange when using an elliptic curve + cipher with TLS; false otherwise. The default implementation returns false. + + \note The meaning of curve identifier is implementation-specific. +*/ bool QTlsBackend::isTlsNamedCurve(int cid) const { Q_UNUSED(cid); @@ -408,6 +547,16 @@ bool QTlsBackend::isTlsNamedCurve(int cid) const return false; } +/*! + \internal + If this backend supports the class QSslDiffieHellmanParameters, this function is + needed for construction of a QSslDiffieHellmanParameters from DER encoded data. + This function is expected to return a value that matches an enumerator in + QSslDiffieHellmanParameters::Error enumeration. The default implementation of this + function returns 0 (equals to QSslDiffieHellmanParameters::NoError). + + \sa QSslDiffieHellmanParameters, implementedClasses() +*/ int QTlsBackend::dhParametersFromDer(const QByteArray &derData, QByteArray *data) const { Q_UNUSED(derData); @@ -416,6 +565,16 @@ int QTlsBackend::dhParametersFromDer(const QByteArray &derData, QByteArray *data return {}; } +/*! + \internal + If this backend supports the class QSslDiffieHellmanParameters, this function is + is needed for construction of a QSslDiffieHellmanParameters from PEM encoded data. + This function is expected to return a value that matches an enumerator in + QSslDiffieHellmanParameters::Error enumeration. The default implementation of this + function returns 0 (equals to QSslDiffieHellmanParameters::NoError). + + \sa QSslDiffieHellmanParameters, implementedClasses() +*/ int QTlsBackend::dhParametersFromPem(const QByteArray &pemData, QByteArray *data) const { Q_UNUSED(pemData); @@ -424,6 +583,14 @@ int QTlsBackend::dhParametersFromPem(const QByteArray &pemData, QByteArray *data return {}; } +/*! + \internal + Returns a list of names of available backends. + + \note This list contains only properly initialized backends. + + \sa QTlsBackend(), isValid() +*/ QList QTlsBackend::availableBackendNames() { if (!backends()) @@ -432,6 +599,11 @@ QList QTlsBackend::availableBackendNames() return backends->backendNames(); } +/*! + \internal + Returns the name of the backend that QSslSocket() would use by default. If no + backend was found, the function returns an empty string. +*/ QString QTlsBackend::defaultBackendName() { // We prefer OpenSSL as default: @@ -459,6 +631,13 @@ QString QTlsBackend::defaultBackendName() return {}; } +/*! + \internal + Returns a backend named \a backendName, if it exists. + Otherwise, it returns \nullptr. + + \sa backendName(), QSslSocket::availableBackends() +*/ QTlsBackend *QTlsBackend::findBackend(const QString &backendName) { if (!backends()) @@ -471,6 +650,13 @@ QTlsBackend *QTlsBackend::findBackend(const QString &backendName) return nullptr; } +/*! + \internal + Returns the backend that QSslSocket is using. If Qt was built without TLS support, + this function returns a minimal backend that only supports QSslCertificate. + + \sa defaultBackend() +*/ QTlsBackend *QTlsBackend::activeOrAnyBackend() { #if QT_CONFIG(ssl) @@ -480,6 +666,17 @@ QTlsBackend *QTlsBackend::activeOrAnyBackend() #endif // QT_CONFIG(ssl) } +/*! + \internal + Returns a list of TLS and DTLS protocol versions, that a backend named + \a backendName supports. + + \note This list is supposed to also include range-based versions, which + allows negotiation of protocols during the handshake, so that these versions + can be used when configuring QSslSocket (e.g. QSsl::TlsV1_2OrLater). + + \sa QSsl::SslProtocol +*/ QList QTlsBackend::supportedProtocols(const QString &backendName) { if (!backends()) @@ -491,6 +688,15 @@ QList QTlsBackend::supportedProtocols(const QString &backendN return {}; } +/*! + \internal + Returns a list of features that a backend named \a backendName supports. E.g. + a backend may support PSK (pre-shared keys, defined as QSsl::SupportedFeature::Psk) + or ALPN (application layer protocol negotiation, identified by + QSsl::SupportedFeature::ClientSideAlpn or QSsl::SupportedFeature::ServerSideAlpn). + + \sa QSsl::SupportedFeature +*/ QList QTlsBackend::supportedFeatures(const QString &backendName) { if (!backends()) @@ -502,6 +708,14 @@ QList QTlsBackend::supportedFeatures(const QString &back return {}; } +/*! + \internal + Returns a list of classes that a backend named \a backendName supports. E.g. a backend + may implement QSslSocket (QSsl::ImplementedClass::Socket), and QDtls + (QSsl::ImplementedClass::Dtls). + + \sa QSsl::ImplementedClass +*/ QList QTlsBackend::implementedClasses(const QString &backendName) { if (!backends()) @@ -513,6 +727,10 @@ QList QTlsBackend::implementedClasses(const QString &bac return {}; } +/*! + \internal + Auxiliary function. Initializes \a key to use \a keyBackend. +*/ void QTlsBackend::resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend) { #if QT_CONFIG(ssl) @@ -523,6 +741,11 @@ void QTlsBackend::resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend) #endif // QT_CONFIG(ssl) } +/*! + \internal + Auxiliary function. Initializes client-side \a auth using the \a hint, \a hintLength, + \a maxIdentityLength and \a maxPskLen. +*/ void QTlsBackend::setupClientPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *hint, int hintLength, unsigned maxIdentityLen, unsigned maxPskLen) { @@ -542,6 +765,11 @@ void QTlsBackend::setupClientPskAuth(QSslPreSharedKeyAuthenticator *auth, const #endif } +/*! + \internal + Auxiliary function. Initializes server-side \a auth using the \a identity, \a identityHint and + \a maxPskLen. +*/ void QTlsBackend::setupServerPskAuth(QSslPreSharedKeyAuthenticator *auth, const char *identity, const QByteArray &identityHint, unsigned int maxPskLen) { @@ -560,6 +788,14 @@ void QTlsBackend::setupServerPskAuth(QSslPreSharedKeyAuthenticator *auth, const } #if QT_CONFIG(ssl) +/*! + \internal + Auxiliary function. Creates a new QSslCipher from \a descriptionOneLine, \a bits + and \a supportedBits. \a descriptionOneLine consists of several fields, separated by + whitespace. These include: cipher name, protocol version, key exchange method, + authentication method, encryption method, message digest (Mac). Example: + "ECDHE-RSA-AES256-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD" +*/ QSslCipher QTlsBackend::createCiphersuite(const QString &descriptionOneLine, int bits, int supportedBits) { QSslCipher ciph; @@ -596,6 +832,14 @@ QSslCipher QTlsBackend::createCiphersuite(const QString &descriptionOneLine, int return ciph; } +/*! + \internal + Auxiliary function. Creates a new QSslCipher from \a suiteName, \a protocol version and + \a protocolString. For example: + \code + createCiphersuite(QLatin1String("ECDHE-RSA-AES256-GCM-SHA256"), QSsl::TlsV1_2, QLatin1String("TLSv1.2")); + \endcode +*/ QSslCipher QTlsBackend::createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol, const QString &protocolString) { @@ -658,6 +902,14 @@ QSslCipher QTlsBackend::createCiphersuite(const QString &suiteName, QSsl::SslPro return ciph; } +/*! + \internal + Auxiliary function. Creates a new QSslCipher from \a name (which is an implementation-specific + string), \a protocol and \a protocolString, e.g.: + \code + createCipher(QStringLiteral("schannel"), QSsl::TlsV1_2, QLatin1String("TLSv1.2")); + \endcode +*/ QSslCipher QTlsBackend::createCipher(const QString &name, QSsl::SslProtocol protocol, const QString &protocolString) { @@ -671,64 +923,123 @@ QSslCipher QTlsBackend::createCipher(const QString &name, QSsl::SslProtocol prot return cipher; } +/*! + \internal + Returns an implementation-specific list of ciphersuites that can be used by QSslSocket. + + \sa QSslConfiguration::defaultCiphers() +*/ QList QTlsBackend::defaultCiphers() { return QSslSocketPrivate::defaultCiphers(); } +/*! + \internal + Returns an implementation-specific list of ciphersuites that can be used by QDtls. +*/ QList QTlsBackend::defaultDtlsCiphers() { return QSslSocketPrivate::defaultDtlsCiphers(); } +/*! + \internal + Sets \a ciphers as defaults ciphers that QSslSocket can use. + + \sa defaultCiphers() +*/ void QTlsBackend::setDefaultCiphers(const QList &ciphers) { QSslSocketPrivate::setDefaultCiphers(ciphers); } +/*! + \internal + Sets \a ciphers as defaults ciphers that QDtls can use. + + \sa defaultDtlsCiphers() +*/ void QTlsBackend::setDefaultDtlsCiphers(const QList &ciphers) { QSslSocketPrivate::setDefaultDtlsCiphers(ciphers); } +/*! + \internal + Sets \a ciphers as a list of supported ciphers. + + \sa QSslConfiguration::supportedCiphers() +*/ void QTlsBackend::setDefaultSupportedCiphers(const QList &ciphers) { QSslSocketPrivate::setDefaultSupportedCiphers(ciphers); } +/*! + \internal + Sets the list of QSslEllipticCurve objects, that QSslConfiguration::supportedEllipticCurves() + returns, to ones that are supported by this backend. +*/ void QTlsBackend::resetDefaultEllipticCurves() { QSslSocketPrivate::resetDefaultEllipticCurves(); } +/*! + Sets \a certs as a list of certificates, that QSslConfiguration::caCertificates() + reports. + + \sa QSslConfiguration::defaultConfiguration(), QSslConfiguration::caCertificates() +*/ void QTlsBackend::setDefaultCaCertificates(const QList &certs) { QSslSocketPrivate::setDefaultCaCertificates(certs); } +/*! + \internal + Returns true if \a configuration allows loading root certificates on demand. +*/ bool QTlsBackend::rootLoadingOnDemandAllowed(const QSslConfiguration &configuration) { return configuration.d->allowRootCertOnDemandLoading; } +/*! + \internal + Stores \a peerCert in the \a configuration. +*/ void QTlsBackend::storePeerCertificate(QSslConfiguration &configuration, const QSslCertificate &peerCert) { configuration.d->peerCertificate = peerCert; } +/*! + \internal + Stores \a peerChain in the \a configuration. +*/ void QTlsBackend::storePeerCertificateChain(QSslConfiguration &configuration, const QList &peerChain) { configuration.d->peerCertificateChain = peerChain; } +/*! + \internal + Clears the peer certificate chain in \a configuration. +*/ void QTlsBackend::clearPeerCertificates(QSslConfiguration &configuration) { configuration.d->peerCertificate.clear(); configuration.d->peerCertificateChain.clear(); } +/*! + \internal + Clears the peer certificate chain in \a d. +*/ void QTlsBackend::clearPeerCertificates(QSslSocketPrivate *d) { Q_ASSERT(d); @@ -736,42 +1047,74 @@ void QTlsBackend::clearPeerCertificates(QSslSocketPrivate *d) d->configuration.peerCertificateChain.clear(); } +/*! + \internal + Updates the configuration in \a d with \a shared value. +*/ void QTlsBackend::setPeerSessionShared(QSslSocketPrivate *d, bool shared) { Q_ASSERT(d); d->configuration.peerSessionShared = shared; } +/*! + \internal + Sets TLS session in \a d to \a asn1. +*/ void QTlsBackend::setSessionAsn1(QSslSocketPrivate *d, const QByteArray &asn1) { Q_ASSERT(d); d->configuration.sslSession = asn1; } +/*! + \internal + Sets TLS session lifetime hint in \a d to \a hint. +*/ void QTlsBackend::setSessionLifetimeHint(QSslSocketPrivate *d, int hint) { Q_ASSERT(d); d->configuration.sslSessionTicketLifeTimeHint = hint; } +/*! + \internal + Sets application layer protocol negotiation status in \a d to \a st. +*/ void QTlsBackend::setAlpnStatus(QSslSocketPrivate *d, AlpnNegotiationStatus st) { Q_ASSERT(d); d->configuration.nextProtocolNegotiationStatus = st; } +/*! + \internal + Sets \a protocol in \a d as a negotiated application layer protocol. +*/ void QTlsBackend::setNegotiatedProtocol(QSslSocketPrivate *d, const QByteArray &protocol) { Q_ASSERT(d); d->configuration.nextNegotiatedProtocol = protocol; } +/*! + \internal + Stores \a peerCert in the TLS configuration of \a d. +*/ void QTlsBackend::storePeerCertificate(QSslSocketPrivate *d, const QSslCertificate &peerCert) { Q_ASSERT(d); d->configuration.peerCertificate = peerCert; } +/*! + \internal + + Stores \a peerChain in the TLS configuration of \a d. + + \note This is a helper function that TlsCryptograph and DtlsCryptograph + call during a handshake. +*/ void QTlsBackend::storePeerCertificateChain(QSslSocketPrivate *d, const QList &peerChain) { @@ -779,6 +1122,13 @@ void QTlsBackend::storePeerCertificateChain(QSslSocketPrivate *d, d->configuration.peerCertificateChain = peerChain; } +/*! + \internal + + Adds \a rootCert to the list of trusted root certificates in \a d. + + \note In Qt 6.1 it's only used on Windows, during so called 'CA fetch'. +*/ void QTlsBackend::addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &rootCert) { Q_ASSERT(d); @@ -786,16 +1136,1222 @@ void QTlsBackend::addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &roo d->configuration.caCertificates += rootCert; } +/*! + \internal + + Saves ephemeral \a key in \a d. + + \sa QSslConfiguration::ephemeralKey() +*/ void QTlsBackend::setEphemeralKey(QSslSocketPrivate *d, const QSslKey &key) { Q_ASSERT(d); d->configuration.ephemeralServerKey = key; } +/*! + \internal + + Implementation-specific. Sets the security level suitable for Qt's + auto-tests. +*/ void QTlsBackend::forceAutotestSecurityLevel() { } +#endif // QT_CONFIG(ssl) + +namespace QTlsPrivate { + +/*! + \internal (Network-private) + \namespace QTlsPrivate + \brief Namespace containing onternal types that TLS backends implement. + + This namespace is private to Qt and the backends that implement its TLS support. +*/ + +/*! + \class TlsKey + \internal (Network-private) + \brief TlsKey is an abstract class, that allows a TLS plugin to provide + an underlying implementation for the class QSslKey. + + Most functions in the class TlsKey are pure virtual and thus have to be + reimplemented by a TLS backend that supports QSslKey. In many cases an + empty implementation as an overrider is sufficient, albeit with some + of QSslKey's functionality missing. + + \sa QTlsBackend::createKey(), QTlsBackend::implementedClasses(), QSslKey +*/ + +/*! + \fn void TlsKey::decodeDer(KeyType type, KeyAlgorithm algorithm, const QByteArray &der, const QByteArray &passPhrase, bool deepClear) + \internal + + If a support of public and private keys in DER format is required, this function + must be overridden and should initialize this key using the \a type, \a algorithm, \a der + and \a passPhrase. If this key was initialized previously, \a deepClear + has an implementation-specific meaning (e.g., if an implementation is using + reference-counting and can share internally some data structures, a value \c true may + trigger decrementing a reference counter on some implementation-specific object). + + \note An empty overrider is sufficient, but then reading keys in QSsl::Der format + will not be supported. + + \sa isNull(), QSsl::KeyType, QSsl::EncodingFormat, QSsl::KeyAlgorithm +*/ + +/*! + \fn void TlsKey::decodePem(KeyType type, KeyAlgorithm algorithm, const QByteArray &pem, const QByteArray &passPhrase, bool deepClear) + \internal + + If a support of public and private keys in PEM format is required, this function must + be overridden and should initialize this key using the \a type, \a algorithm, \a pem and + \a passPhrase. If this key was initialized previously, \a deepClear has an + implementation-specific meaning (e.g., in an implementation using reference-counting, + a value \c true may trigger decrementing a reference counter on some implementation-specific + object). + + \note An empty overrider is sufficient, but then reading keys in QSsl::Pem format + will not be supported. + + \sa isNull(), QSsl::KeyType, QSsl::EncodingFormat, QSsl::KeyAlgorithm +*/ + +/*! + \fn QByteArray TlsKey::toPem(const QByteArray &passPhrase) const + \internal + + This function must be overridden, if converting a key to PEM format, potentially with + encryption, is needed (e.g. to save a QSslKey into a file). If this key is + private and \a passPhrase is not empty, the key's data is expected to be encrypted using + some conventional encryption algorithm (e.g. DES or AES - the one that different tools + or even the class QSslKey can understand later). + + \note If this particular functionality is not needed, an overrider returning an + empty QByteArray is sufficient. + + \sa QSslKey::toPem() +*/ + +/*! + \fn QByteArray TlsKey::derFromPem(const QByteArray &pem, QMap *headers) const + \internal + + Converts \a pem to DER format, using this key's type and algorithm. The parameter \a headers + must be a valid, non-null pointer. When parsing \a pem, the headers found there will be saved + into \a headers. + + \note An overrider returning an empty QByteArray is sufficient, if QSslKey::toDer() is not + needed. + + \note This function is very implementation-specific. A backend, that already has this key's + non-empty DER data, may simply return this data. + + \sa QSslKey::toDer() +*/ + +/*! + \fn QByteArray TlsKey::pemFromDer(const QByteArray &der, const QMap &headers) const + \internal + + If overridden, this function is expected to convert \a der, using \a headers, to PEM format. + + \note This function is very implementation-specific. As of now (Qt 6.1), it is only required by + Qt's own non-OpenSSL backends, that internally use DER and implement QSslKey::toPem() + via pemFromDer(). +*/ + +/*! + \fn void TlsKey::fromHandle(Qt::HANDLE handle, KeyType type) + \internal + + Initializes this key using the \a handle and \a type, taking the ownership + of the \a handle. + + \note The meaning of the \a handle is implementation-specific. + + \note If a TLS backend does not support such keys, it must provide an + empty implementation. + + \sa handle(), QSslKey::QSslKey(), QSslKet::handle() +*/ + +/*! + \fn TlsKey::handle() const + \internal + + If a TLS backend supports opaque keys, returns a native handle that + this key was initialized with. + + \sa fromHandle(), QSslKey::handle() +*/ + +/*! + \fn bool TlsKey::isNull() const + \internal + + Returns \c true if this is a null key, \c false otherwise. + + \note A null key corresponds to the default-constructed + QSslKey or the one, that was cleared via QSslKey::clear(). + + \sa QSslKey::isNull() +*/ + +/*! + \fn QSsl::KeyType TlsKey::type() const + \internal + + Returns the type of this key (public or private). +*/ + +/*! + \fn QSsl::KeyAlgorithm TlsKey::algorithm() const + \internal + + Return this key's algorithm. +*/ + +/*! + \fn int TlsKey::length() const + \internal + + Returns the length of the key in bits, or -1 if the key is null. +*/ + +/*! + \fn void TlsKey::clear(bool deep) + \internal + + Clears the contents of this key, making it a null key. The meaning + of \a deep is implementation-specific (e.g. if some internal objects + representing a key can be shared using reference counting, \a deep equal + to \c true would imply decrementing a reference count). + + \sa isNull() +*/ + +/*! + \fn bool TlsKey::isPkcs8() const + \internal + + This function is internally used only by Qt's own TLS plugins and affects + the way PEM file is generated by TlsKey. It's sufficient to override it + and return \c false in case a new TLS backend is not using Qt's plugin + as a base. +*/ + +/*! + \fn QByteArray TlsKey::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &passPhrase, const QByteArray &iv) const + \internal + + This function allows to decrypt \a data (for example, a private key read from a file), using + \a passPhrase, initialization vector \a iv. \a cipher is describing a block cipher and its + mode (for example, AES256 + CBC). decrypt() is needed to implement QSslKey's constructor. + + \note A TLS backend may provide an empty implementation, but as a result QSslKey will not be able + to work with private encrypted keys. + + \sa QSslKey +*/ + +/*! + QByteArray TlsKey::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &passPhrase, const QByteArray &iv) const + + This function is needed to implement QSslKey::toPem() with encryption (for a private + key). \a cipher names a block cipher to use to encrypt \a data, using + \a passPhrase and initialization vector \a iv. + + \note An empty implementation is sufficient, but QSslKey::toPem() will fail for + a private key and non-empty passphrase. + + \sa QSslKey +*/ + +/*! + \internal + + Destroys this key. +*/ +TlsKey::~TlsKey() = default; + +/*! + \internal + + A convenience function that returns a string, corresponding to the + key type or algorithm, which can be used as a header in a PEM file. +*/ +QByteArray TlsKey::pemHeader() const +{ + if (type() == QSsl::PublicKey) + return QByteArrayLiteral("-----BEGIN PUBLIC KEY-----"); + else if (algorithm() == QSsl::Rsa) + return QByteArrayLiteral("-----BEGIN RSA PRIVATE KEY-----"); + else if (algorithm() == QSsl::Dsa) + return QByteArrayLiteral("-----BEGIN DSA PRIVATE KEY-----"); + else if (algorithm() == QSsl::Ec) + return QByteArrayLiteral("-----BEGIN EC PRIVATE KEY-----"); + else if (algorithm() == QSsl::Dh) + return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----"); + + Q_UNREACHABLE(); + return {}; +} + +/*! + \internal + A convenience function that returns a string, corresponding to the + key type or algorithm, which can be used as a footer in a PEM file. +*/ +QByteArray TlsKey::pemFooter() const +{ + if (type() == QSsl::PublicKey) + return QByteArrayLiteral("-----END PUBLIC KEY-----"); + else if (algorithm() == QSsl::Rsa) + return QByteArrayLiteral("-----END RSA PRIVATE KEY-----"); + else if (algorithm() == QSsl::Dsa) + return QByteArrayLiteral("-----END DSA PRIVATE KEY-----"); + else if (algorithm() == QSsl::Ec) + return QByteArrayLiteral("-----END EC PRIVATE KEY-----"); + else if (algorithm() == QSsl::Dh) + return QByteArrayLiteral("-----END PRIVATE KEY-----"); + + Q_UNREACHABLE(); + return {}; +} + +/*! + \class X509Certificate + \internal (Network-private) + \brief X509Certificate is an abstract class that allows a TLS backend to + provide an implementation of the QSslCertificate class. + + This class provides an interface that must be reimplemented by a TLS plugin, + that supports QSslCertificate. Most functions are pure virtual, and thus + have to be overridden. For some of them, an empty overrider is acceptable, + though a part of functionality in QSslCertificate will be missing. + + \sa QTlsBackend::createCertificate(), QTlsBackend::X509PemReader(), QTlsBackend::X509DerReader() +*/ + +/*! + \fn bool X509Certificate::isEqual(const X509Certificate &other) const + \internal + + This function is expected to return \c true if this certificate is the same as + the \a other, \c false otherwise. Used by QSslCertificate's comparison operators. +*/ + +/*! + \fn bool X509Certificate::isNull() const + \internal + + Returns true if this certificate was default-constructed and not initialized yet. + This function is called by QSslCertificate::isNull(). + + \sa QSslCertificate::isNull() +*/ + +/*! + \fn bool X509Certificate::isSelfSigned() const + \internal + + This function is needed to implement QSslCertificate::isSelfSigned() + + \sa QSslCertificate::isSelfSigned() +*/ + +/*! + \fn QByteArray X509Certificate::version() const + \internal + + Implements QSslCertificate::version(). + + \sa QSslCertificate::version() +*/ + +/*! + \fn QByteArray X509Certificate::serialNumber() const + \internal + + This function is expected to return the certificate's serial number string in + hexadecimal format. + + \sa QSslCertificate::serialNumber() +*/ + +/*! + \fn QStringList X509Certificate::issuerInfo(QSslCertificate::SubjectInfo subject) const + \internal + + This function is expected to return the issuer information for the \a subject + from the certificate, or an empty list if there is no information for subject + in the certificate. There can be more than one entry of each type. + + \sa QSslCertificate::issuerInfo(). +*/ + +/*! + \fn QStringList X509Certificate::issuerInfo(const QByteArray &attribute) const + \internal + + This function is excpected to return the issuer information for attribute from + the certificate, or an empty list if there is no information for \a attribute + in the certificate. There can be more than one entry for an attribute. + + \sa QSslCertificate::issuerInfo(). +*/ + +/*! + \fn QStringList X509Certificate::subjectInfo(QSslCertificate::SubjectInfo subject) const + \internal + + This function is expected to return the information for the \a subject, or an empty list + if there is no information for subject in the certificate. There can be more than one + entry of each type. + + \sa QSslCertificate::subjectInfo(). +*/ + +/*! + \fn QStringList X509Certificate::subjectInfo(const QByteArray &attribute) const + \internal + + This function is expected to return the subject information for \a attribute, or + an empty list if there is no information for attribute in the certificate. + There can be more than one entry for an attribute. + + \sa QSslCertificate::subjectInfo(). +*/ + +/*! + \fn QList X509Certificate::subjectInfoAttributes() const + \internal + + This function is expected to return a list of the attributes that have values + in the subject information of this certificate. The information associated + with a given attribute can be accessed using the subjectInfo() method. Note + that this list may include the OIDs for any elements that are not known by + the TLS backend. + + \note This function is needed for QSslCertificate:::subjectInfoAttributes(). + + \sa subjectInfo() +*/ + +/*! + \fn QList X509Certificate::issuerInfoAttributes() const + \internal + + This function is expected to return a list of the attributes that have + values in the issuer information of this certificate. The information + associated with a given attribute can be accessed using the issuerInfo() + method. Note that this list may include the OIDs for any + elements that are not known by the TLS backend. + + \note This function implements QSslCertificate::issuerInfoAttributes(). + + \sa issuerInfo() +*/ + +/*! + \fn QMultiMap X509Certificate::subjectAlternativeNames() const + \internal + + This function is expected to return the list of alternative subject names for + this certificate. The alternative names typically contain host names, optionally + with wildcards, that are valid for this certificate. + + \sa subjectInfo() +*/ + +/*! + \fn QDateTime X509Certificate::effectiveDate() const + \internal + + This function is expected to return the date-time that the certificate + becomes valid, or an empty QDateTime if this is a null certificate. + + \sa expiryDate() +*/ + +/*! + \fn QDateTime X509Certificate::expiryDate() const + \internal + + This function is expected to return the date-time that the certificate expires, + or an empty QDateTime if this is a null certificate. + + \sa effectiveDate() +*/ + +/*! + \fn qsizetype X509Certificate::numberOfExtensions() const + \internal + + This function is expected to return the number of X509 extensions of + this certificate. +*/ + +/*! + \fn QString X509Certificate::oidForExtension(qsizetype i) const + \internal + + This function is expected to return the ASN.1 OID for the extension + with index \a i. + + \sa numberOfExtensions() +*/ + +/*! + \fn QString X509Certificate::nameForExtension(qsizetype i) const + \internal + + This function is expected to return the name for the extension + with index \a i. If no name is known for the extension then the + OID will be returned. + + \sa numberOfExtensions(), oidForExtension() +*/ + +/*! + \fn QVariant X509Certificate::valueForExtension(qsizetype i) const + \internal + + This function is expected to return the value of the extension + with index \a i. The structure of the value returned depends on + the extension type + + \sa numberOfExtensions() +*/ + +/*! + \fn bool X509Certificate::isExtensionCritical(qsizetype i) const + \internal + + This function is expected to return the criticality of the extension + with index \a i. + + \sa numberOfExtensions() +*/ + +/*! + \fn bool X509Certificate::isExtensionSupported(qsizetype i) const + \internal + + This function is expected to return \c true if this extension is supported. + In this case, supported simply means that the structure of the QVariant returned + by the valueForExtension() accessor will remain unchanged between versions. + + \sa numberOfExtensions() +*/ + +/*! + \fn QByteArray X509Certificate::toPem() const + \internal + + This function is expected to return this certificate converted to a PEM (Base64) + encoded representation. +*/ + +/*! + \fn QByteArray X509Certificate::toDer() const + \internal + + This function is expected to return this certificate converted to a DER (binary) + encoded representation. +*/ + +/*! + \fn QString X509Certificate::toText() const + \internal + + This function is expected to return this certificate converted to a human-readable + text representation. +*/ + +/*! + \fn Qt::HANDLE X509Certificate::handle() const + \internal + + This function is expected to return a pointer to the native certificate handle, + if there is one, else nullptr. +*/ + +/*! + \fn size_t X509Certificate::hash(size_t seed) const + \internal + + This function is expected to return the hash value for this certificate, + using \a seed to seed the calculation. +*/ + +/*! + \internal + + Destroys this certificate. +*/ +X509Certificate::~X509Certificate() = default; + +/*! + \internal + + Returns the certificate subject's public key. +*/ +TlsKey *X509Certificate::publicKey() const +{ + return nullptr; +} + +#if QT_CONFIG(ssl) + +/*! + \class TlsCryptograph + \internal (Network-private) + \brief TlsCryptograph is an abstract class, that allows a TLS pluging to implement QSslSocket. + + This abstract base class provides an interface that must be reimplemented by a TLS plugin, + that supports QSslSocket. A class, implementing TlsCryptograph's interface, is responsible + for TLS handshake, reading and writing encryped application data; it is expected + to work with QSslSocket and it's private implementation - QSslSocketPrivate. + QSslSocketPrivate provides access to its read/write buffers, QTcpSocket it + internally uses for connecting, reading and writing. QSslSocketPrivate + can also be used for reporting errors and storing the certificates received + during the handshake phase. + + \note Most of the functions in this class are pure virtual and have no actual implementation + in the QtNetwork module. This documentation is mostly conceptual and only describes what those + functions are expected to do, but not how they must be implemented. + + \sa QTlsBackend::createTlsCryptograph() +*/ + +/*! + \fn void TlsCryptograph::init(QSslSocket *q, QSslSocketPrivate *d) + \internal + + When initializing this TlsCryptograph, QSslSocket will pass a pointer to self and + its d-object using this function. +*/ + +/*! + \fn QList TlsCryptograph::tlsErrors() const + \internal + + Returns a list of QSslError, describing errors encountered during + the TLS handshake. + + \sa QSslSocket::sslHandshakeErrors() +*/ + +/*! + \fn void TlsCryptograph::startClientEncryption() + \internal + + A client-side QSslSocket calls this function after its internal TCP socket + establishes a connection with a remote host, or from QSslSocket::startClientEncryption(). + This TlsCryptograph is expected to initialize some implementation-specific TLS context, + if needed, and then start the client side of the TLS handshake (for example, by calling + transmit()), using TCP socket from QSslSocketPrivate. + + \sa init(), transmit(), QSslSocket::startClientEncryption(), QSslSocket::connectToHostEncrypted() +*/ + +/*! + \fn void TlsCryptograph::startServerEncryption() + \internal + + This function is called by QSslSocket::startServerEncryption(). The TlsCryptograph + is expected to initialize some implementation-specific TLS context, if needed, + and then try to read the ClientHello message and continue the TLS handshake + (for example, by calling transmit()). + + \sa transmit(), QSslSocket::startServerEncryption() +*/ + +/*! + \fn void TlsCryptograph::continueHandshake() + \internal + + QSslSocket::resume() calls this function if its pause mode is QAbstractSocket::PauseOnSslErrors, + and errors, found during the handshake, were ignored. If implemented, this function is expected + to emit QSslSocket::encrypted(). + + \sa QAbstractSocket::pauseMode(), QSslSocket::sslHandshakeErrors(), QSslSocket::ignoreSslErrors(), QSslSocket::resume() +*/ + +/*! + \fn void TlsCryptograph::disconnectFromHost() + \internal + + This function is expected to call disconnectFromHost() on the TCP socket + that can be obtained from QSslSocketPrivate. Any additional actions + are implementation-specific (e.g., sending shutdown alert message). + +*/ + +/*! + \fn void TlsCryptograph::disconnected() + \internal + + This function is called when the remote has disconnected. If there + is data left to be read you may ignore the maxReadBufferSize restriction + and read it all now. +*/ + +/*! + \fn QSslCipher TlsCryptograph::sessionCipher() const + \internal + + This function returns a QSslCipher object describing the ciphersuite negotiated + during the handshake. +*/ + +/*! + \fn QSsl::SslProtocol TlsCryptograph::sessionProtocol() const + \internal + + This function returns the version of TLS (or DTLS) protocol negotiated during the handshake. +*/ + +/*! + \fn void TlsCryptograph::transmit() + \internal + + This function is responsible for reading and writing data. The meaning of these I/O + operations depends on an implementation-specific TLS state machine. These read and write + operations can be reading and writing parts of a TLS handshake (e.g. by calling handshake-specific + functions), or reading and writing application data (if encrypted connection was already + established). transmit() is expected to use the QSslSocket's TCP socket (accessible via + QSslSocketPrivate) to read the incoming data and write the outgoing data. When in encrypted + state, transmit() is also using QSslSocket's internal read and write buffers: the read buffer + to fill with decrypted incoming data; the write buffer - for the data to encrypt and send. + This TlsCryptograph can also use QSslSocketPrivate to check which TLS errors were ignored during + the handshake. + + \note This function is responsible for emitting QSslSocket's signals, that occur during the + handshake (e.g. QSslSocket::sslErrors() or QSslSocket::encrytped()), and also read/write signals, + e.g. QSslSocket::bytesWritten() and QSslSocket::readyRead(). + + \sa init() +*/ + +/*! + \internal + + Destroys this object. +*/ +TlsCryptograph::~TlsCryptograph() = default; + +/*! + \internal + + This function allows to share QSslContext between several QSslSocket objects. + The default implementation does nothing. + + \note The definition of the class QSslContext is implementation-specific. + + \sa sslContext() +*/ +void TlsCryptograph::checkSettingSslContext(QSharedPointer tlsContext) +{ + Q_UNUSED(tlsContext); +} + +/*! + \internal + + Returns the context previously set by checkSettingSslContext() or \nullptr, + if no context was set. The default implementation returns \nullptr. + + \sa checkSettingSslContext() +*/ +QSharedPointer TlsCryptograph::sslContext() const +{ + return {}; +} + +/*! + \internal + + If this TLS backend supports reporting errors before handshake is finished, + e.g. from a verification callback function, enableHandshakeContinuation() + allows this object to continue handshake. The default implementation does + nothing. + + \sa QSslSocket::handshakeInterruptedOnError(), QSslConfiguration::setHandshakeMustInterruptOnError() +*/ +void TlsCryptograph::enableHandshakeContinuation() +{ +} + +/*! + \internal + + Windows and OpenSSL-specific, only used internally by Qt's OpenSSL TLS backend. + + \note The default empty implementation is sufficient. +*/ +void TlsCryptograph::cancelCAFetch() +{ +} + +/*! + \internal + + Windows and Schannel-specific, only used by Qt's Schannel TLS backend, in + general, if a backend has its own buffer where it stores undecrypted data + then it must report true if it contains any data through this function. + + \note The default empty implementation, returning \c false is sufficient. +*/ +bool TlsCryptograph::hasUndecryptedData() const +{ + return false; +} + +/*! + \internal + + Returns the list of OCSP (Online Certificate Status Protocol) responses, + received during the handshake. The default implementation returns an empty + list. +*/ +QList TlsCryptograph::ocsps() const +{ + return {}; +} + +/*! + \internal + + A helper function that can be used during a handshake. Returns \c true if the \a peerName + matches one of subject alternative names or commond names found in the \a certificate. +*/ +bool TlsCryptograph::isMatchingHostname(const QSslCertificate &certificate, const QString &peerName) +{ + return QSslSocketPrivate::isMatchingHostname(certificate, peerName); +} + +/*! + \internal + Calls QAbstractSocketPrivate::setErrorAndEmit() for \a d, passing \a errorCode and + \a errorDescription as parameters. +*/ +void TlsCryptograph::setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode, + const QString &errorDescription) const +{ + Q_ASSERT(d); + d->setErrorAndEmit(errorCode, errorDescription); +} + +#if QT_CONFIG(dtls) +/*! + \class DtlsBase + \internal (Network-private) + \brief DtlsBase is a base class for the classes DtlsCryptograph and DtlsCookieVerifier. + + DtlsBase is the base class for the classes DtlsCryptograph and DtlsCookieVerifier. It's + an abstract class, an interface that these before-mentioned classes share. It allows to + set, get and clear the last error that occurred, set and get cookie generation parameters, + set and get QSslConfiguration. + + \note This class is not supposed to be inherited directly, it's only needed by DtlsCryptograph + and DtlsCookieVerifier. + + \sa QDtls, QDtlsClientVerifier, DtlsCryptograph, DtlsCookieVerifier +*/ + +/*! + \fn void DtlsBase::setDtlsError(QDtlsError code, const QString &description) + \internal + + Sets the last error to \a code and its textual description to \a description. + + \sa QDtlsError, error(), errorString() +*/ + +/*! + \fn QDtlsError DtlsBase::error() const + \internal + + This function, when overridden, is expected to return the code for the last error that occurred. + If no error occurred it should return QDtlsError::NoError. + + \sa QDtlsError, errorString(), setDtlsError() +*/ + +/*! + \fn QDtlsError DtlsBase::errorString() const + \internal + + This function, when overridden, is expected to return the textual description for the last error + that occurred or an empty string if no error occurred. + + \sa QDtlsError, error(), setDtlsError() +*/ + +/*! + \fn void DtlsBase::clearDtlsError() + \internal + + This function is expected to set the error code for the last error to QDtlsError::NoError and + its textual description to an empty string. + + \sa QDtlsError, setDtlsError(), error(), errorString() +*/ + +/*! + \fn void DtlsBase::setConfiguration(const QSslConfiguration &configuration) + \internal + + Sets a TLS configuration that an object of a class inheriting from DtlsCookieVerifier or + DtlsCryptograph will use, to \a configuration. + + \sa configuration() +*/ + +/*! + \fn QSslConfiguration DtlsBase::configuration() const + \internal + + Returns TLS configuration this object is using (either set by setConfiguration() + previously, or the default DTLS configuration). + + \sa setConfiguration(), QSslConfiguration::defaultDtlsConfiguration() +*/ + +/*! + \fn bool DtlsBase::setCookieGeneratorParameters(const QDtlsClientVerifier::GeneratorParameters ¶ms) + \internal + + Sets the DTLS cookie generation parameters that DtlsCookieVerifier or DtlsCryptograph will use to + \a params. + + \note This function returns \c false if parameters were invalid - if the secret was empty. Otherwise, + this function must return true. + + \sa QDtlsClientVerifier::GeneratorParameters, cookieGeneratorParameters() +*/ + +/*! + \fn QDtlsClientVerifier::GeneratorParameters DtlsBase::cookieGeneratorParameters() const + \internal + + Returns DTLS cookie generation parameters that were either previously set by setCookieGeneratorParameters(), + or default parameters. + + \sa setCookieGeneratorParameters() +*/ + +/*! + \internal + + Destroys this object. +*/ +DtlsBase::~DtlsBase() = default; + +/*! + \class DtlsCookieVerifier + \internal (Network-private) + \brief DtlsCookieVerifier is an interface that allows a TLS plugin to support the class QDtlsClientVerifier. + + DtlsCookieVerifier is an interface, an abstract class, that has to be implemented by + a TLS plugin that supports DTLS cookie verification. + + \sa QDtlsClientVerifier +*/ + +/*! + \fn bool DtlsCookieVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port) + \internal + + This function is expected to verify a ClientHello message, found in \a dgram, using \a address, + \a port, and cookie generator parameters. The function returns \c true if such cookie was found + and \c false otherwise. If no valid cookie was found in the \a dgram, this verifier should use + \a socket to send a HelloVerifyRequest message, using \a address and \a port as the destination + and a source material for cookie generation, see also + \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1} + + \sa QDtlsClientVerifier +*/ + +/*! + \fn QByteArray DtlsCookieVerifier::verifiedHello() const + \internal + + Returns the last ClientHello message containing the DTLS cookie that this verifier was + able to verify as correct, or an empty byte array. + + \sa verifyClient() +*/ + +/*! + \class DtlsCryptograph + \internal (Network-private) + \brief DtlsCryptograph is an interface that allows a TLS plugin to implement the class QDtls. + + DtlsCryptograph is an abstract class; a TLS plugin can provide a class, inheriting from + DtlsCryptograph and implementing its pure virtual functions, thus implementing the class + QDtls and enabling DTLS over UDP. + + To write DTLS datagrams, a class, inheriting DtlsCryptograph, is expected to use + QUdpSocket. In general, all reading is done externally, so DtlsCryptograph is + expected to only write into QUdpSocket, check possible socket errors, change socket + options if needed. + + \note All functions in this class are pure virtual and have no actual implementation + in the QtNetwork module. This documentation is mostly conceptual and only describes + what those functions are expected to do, but not how they must be implemented. + + \sa QDtls, QUdpSocket +*/ + +/*! + \fn QSslSocket::SslMode DtlsCryptograph::cryptographMode() const + \internal + + Returns the mode (client or server) this object operates in. + + \note This mode is set once when a new DtlsCryptograph is created + by QTlsBackend and cannot change. + + \sa QTlsBackend::createDtlsCryptograph() +*/ + +/*! + \fn void DtlsCryptograph::setPeer(const QHostAddress &addr, quint16 port, const QString &name) + \internal + + Sets the remote peer's address to \a addr and remote port to \a port. \a name, + if not empty, is to be used when validating the peer's certificate. + + \sa peerAddress(), peerPort(), peerVerificationName() +*/ + +/*! + \fn QHostAddress DtlsCryptograph::peerAddress() const + \internal + + Returns the remote peer's address previously set by setPeer() or, + if no address was set, an empty address. + + \sa setPeer() +*/ + +/*! + \fn quint16 DtlsCryptograph::peerPort() const + \internal + + Returns the remote peer's port previously set by setPeer() or + 0 if no port was set. + + \sa setPeer(), peerAddress() +*/ + +/*! + \fn void DtlsCryptograph::setPeerVerificationName(const QString &name) + \internal + + Sets the host name to use during certificate validation to \a name. + + \sa peerVerificationName(), setPeer() +*/ + +/*! + \fn QString DtlsCryptograph::peerVerificationName() const + \internal + + Returns the name that this object is using during the certificate validation, + previously set by setPeer() or setPeerVerificationName(). Returns an empty string + if no peer verification name was set. + + \sa setPeer(), setPeerVerificationName() +*/ + +/*! + \fn void DtlsCryptograph::setDtlsMtuHint(quint16 mtu) + \internal + + Sets the maximum transmission unit (MTU), if it is supported by a TLS implementation, to \a mtu. + + \sa dtlsMtuHint() +*/ + +/*! + \fn quint16 DtlsCryptograph::dtlsMtuHint() const + \internal + + Returns the value of the maximum transmission unit either previously set by setDtlsMtuHint(), + or some implementation-specific value (guessed or somehow known to this DtlsCryptograph). + + \sa setDtlsMtuHint() +*/ + +/*! + \fn QDtls::HandshakeState DtlsCryptograph::state() const + \internal + + Returns the current handshake state for this DtlsCryptograph (not started, in progress, + peer verification error found, complete). + + \sa isConnectionEncrypted(), startHandshake() +*/ + +/*! + \fn bool DtlsCryptograph::isConnectionEncrypted() const + \internal + + Returns \c true if this DtlsCryptograph has completed a handshake without validation + errors (or these errors were ignored). Returns \c false otherwise. +*/ + +/*! + \fn bool DtlsCryptograph::startHandshake(QUdpSocket *socket, const QByteArray &dgram) + \internal + + This function is expected to initialize some implementation-specific context and to start a DTLS + handshake, using \a socket to write datagrams (but not to read them). If this object is operating + as a server, \a dgram is non-empty and contains the ClientHello message. This function returns + \c true if no error occurred (and this DtlsCryptograph's state switching to + QDtls::HandshakeState::HandshakeInProgress), \c false otherwise. + + \sa continueHandshake(), handleTimeout(), resumeHandshake(), abortHandshake(), state() +*/ + +/*! + \fn bool DtlsCryptograph::handleTimeout(QUdpSocket *socket) + \internal + + In case a timeout occurred during the handshake, allows to re-transmit the last message, + using \a socket to write the datagram. Returns \c true if no error occurred, \c false otherwise. + + \sa QDtls::handshakeTimeout(), QDtls::handleTimeout() +*/ + +/*! + \fn bool DtlsCryptograph::continueHandshake(QUdpSocket *socket, const QByteArray &dgram) + \internal + + Continues the handshake, using \a socket to write datagrams (a handshake-specific message). + \a dgram contains the peer's handshake-specific message. Returns \c false in case some error + was encountered (this can include socket-related errors and errors found during the certificate + validation). Returns \c true if the handshake was complete successfully, or is still in progress. + + This function, depending on the implementation-specific state machine, may leave the handshake + state in QDtls::HandshakeState::HandshakeInProgress, or switch to QDtls::HandshakeState::HandshakeComplete + or QDtls::HandshakeState::PeerVerificationFailed. + + This function may store the peer's certificate (or chain of certificates), extract and store + the information about the negotiated session protocol and ciphersuite. + + \sa startHandshake() +*/ + +/*! + \fn bool DtlsCryptograph::resumeHandshake(QUdpSocket *socket) + \internal + + If peer validation errors were found duing the handshake, this function tries to + continue and complete the handshake. If errors were ignored, the function switches + this object's state to QDtls::HandshakeState::HandshakeComplete and returns \c true. + + \sa abortHandshake() +*/ + +/*! + \fn void DtlsCryptograph::abortHandshake(QUdpSocket *socket) + \internal + + Aborts the handshake if it's in progress or in the state QDtls::HandshakeState::PeerVerificationFailed. + The use of \a socket is implementation-specific (for example, this DtlsCryptograph may send + ShutdownAlert message). + + \sa resumeHandshake() +*/ + +/*! + \fn void DtlsCryptograph::sendShutdownAlert(QUdpSocket *socket) + \internal + + If the underlying TLS library provides the required functionality, this function + may sent ShutdownAlert message using \a socket. +*/ + +/*! + \fn QList DtlsCryptograph::peerVerificationErrors() const + \internal + + Returns the list of errors that this object encountered during DTLS handshake + and certificate validation. + + \sa ignoreVerificationErrors() +*/ + +/*! + \fn void DtlsCryptograph::ignoreVerificationErrors(const QList &errorsToIgnore) + \internal + + Tells this object to ignore errors from \a errorsToIgnore when they are found during + DTLS handshake. + + \sa peerVerificationErrors() +*/ + +/*! + \fn QSslCipher DtlsCryptograph::dtlsSessionCipher() const + \internal + + If such information is available, returns the ciphersuite, negotiated during + the handshake. + + \sa continueHandshake(), dtlsSessionProtocol() +*/ + +/*! + \fn QSsl::SslProtocol DtlsCryptograph::dtlsSessionProtocol() const + \internal + + Returns the version of the session protocol that was negotiated during the handshake or + QSsl::UnknownProtocol if the handshake is incomplete or no information about the session + protocol is available. + + \sa continueHandshake(), dtlsSessionCipher() +*/ + +/*! + \fn qint64 DtlsCryptograph::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) + \internal + + If this DtlsCryptograph is in the QDtls::HandshakeState::HandshakeComplete state, this function + encrypts \a dgram and writes this encrypted data into \a socket. + + Returns the number of bytes (of \a dgram) written, or -1 in case of error. This function should + set the error code and description if some error was encountered. + + \sa decryptDatagram() +*/ + +/*! + \fn QByteArray DtlsCryptograph::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) + \internal + + If this DtlsCryptograph is in the QDtls::HandshakeState::HandshakeComplete state, decrypts \a dgram. + The use of \a socket is implementation-specific. This function should return an empty byte array + and set the error code and description if some error was encountered. +*/ + +#endif // QT_CONFIG(dtls) +#endif // QT_CONFIG(ssl) + +} // namespace QTlsPrivate + +#if QT_CONFIG(ssl) +/*! + \internal +*/ Q_NETWORK_EXPORT void qt_ForceTlsSecurityLevel() { if (auto *backend = QSslSocketPrivate::tlsBackendInUse()) diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h index 1a6110b17b..79bd0c5ebf 100644 --- a/src/network/ssl/qtlsbackend_p.h +++ b/src/network/ssl/qtlsbackend_p.h @@ -94,13 +94,6 @@ class QSslKey; namespace QTlsPrivate { -// The class TlsKey encapsulates key's data (DER) or backend-specific -// data-structure, like RSA/DSA/DH structs in OpenSSL. -// TLSTODO: Interface is mostly what QSslKeyPrivate is now. Names, -// however strange they are, for now preserved to ease the transition -// (this may change in future - for example, 'decodeDer' is not just -// decoding DER, it's initializing a key from DER. Note, QSslKey requires -// a real TLS library because private keys tend to be encrypted. class Q_NETWORK_PRIVATE_EXPORT TlsKey { public: virtual ~TlsKey(); @@ -117,7 +110,7 @@ public: virtual QByteArray derFromPem(const QByteArray &pem, QMap *headers) const = 0; virtual QByteArray pemFromDer(const QByteArray &der, const QMap &headers) const = 0; - virtual void fromHandle(Qt::HANDLE opaque, KeyType type) = 0; + virtual void fromHandle(Qt::HANDLE handle, KeyType type) = 0; virtual Qt::HANDLE handle() const = 0; virtual bool isNull() const = 0; @@ -127,35 +120,30 @@ public: virtual void clear(bool deepClear) = 0; - // Needed by QSslKeyPrivate::pemFromDer() for non-OpenSSL backends. virtual bool isPkcs8() const = 0; virtual QByteArray decrypt(Cipher cipher, const QByteArray &data, - const QByteArray &key, const QByteArray &iv) const = 0; + const QByteArray &passPhrase, const QByteArray &iv) const = 0; virtual QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv) const = 0; - // Those two are non-virtual, always the same and only depend on the key type - // and algorithm: QByteArray pemHeader() const; QByteArray pemFooter() const; }; -// An abstraction hiding OpenSSL's X509 or our generic -// 'derData'-based code. class Q_NETWORK_PRIVATE_EXPORT X509Certificate { public: virtual ~X509Certificate(); - virtual bool isEqual(const X509Certificate &rhs) const = 0; + virtual bool isEqual(const X509Certificate &other) const = 0; virtual bool isNull() const = 0; virtual bool isSelfSigned() const = 0; virtual QByteArray version() const = 0; virtual QByteArray serialNumber() const = 0; - virtual QStringList issuerInfo(QSslCertificate::SubjectInfo info) const = 0; + virtual QStringList issuerInfo(QSslCertificate::SubjectInfo subject) const = 0; virtual QStringList issuerInfo(const QByteArray &attribute) const = 0; - virtual QStringList subjectInfo(QSslCertificate::SubjectInfo info) const = 0; + virtual QStringList subjectInfo(QSslCertificate::SubjectInfo subject) const = 0; virtual QStringList subjectInfo(const QByteArray &attribute) const = 0; virtual QList subjectInfoAttributes() const = 0; @@ -167,13 +155,14 @@ public: virtual TlsKey *publicKey() const; // Extensions. Plugins do not expose internal representation - // and cannot rely on QSslCertificate's internals. + // and cannot rely on QSslCertificate's internals. Thus, + // we provide this information 'in pieces': virtual qsizetype numberOfExtensions() const = 0; - virtual QString oidForExtension(qsizetype index) const = 0; - virtual QString nameForExtension(qsizetype index) const = 0; - virtual QVariant valueForExtension(qsizetype index) const = 0; - virtual bool isExtensionCritical(qsizetype index) const = 0; - virtual bool isExtensionSupported(qsizetype index) const = 0; + virtual QString oidForExtension(qsizetype i) const = 0; + virtual QString nameForExtension(qsizetype i) const = 0; + virtual QVariant valueForExtension(qsizetype i) const = 0; + virtual bool isExtensionCritical(qsizetype i) const = 0; + virtual bool isExtensionSupported(qsizetype i) const = 0; virtual QByteArray toPem() const = 0; virtual QByteArray toDer() const = 0; @@ -225,7 +214,6 @@ public: virtual QList ocsps() const; static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName); - static bool isMatchingHostname(const QString &cn, const QString &hostname); void setErrorAndEmit(QSslSocketPrivate *d, QAbstractSocket::SocketError errorCode, const QString &errorDescription) const; @@ -356,8 +344,9 @@ public: virtual QString longNameForId(int cid) const; virtual bool isTlsNamedCurve(int cid) const; - // TLSTODO: int->enum ugliness in error reporting. - // DH decoding: + // Note: int and not QSslDiffieHellmanParameter::Error - because this class and + // its enum are QT_CONFIG(ssl)-conditioned. But not QTlsBackend and + // its virtual functions. DH decoding: virtual int dhParametersFromDer(const QByteArray &derData, QByteArray *data) const; virtual int dhParametersFromPem(const QByteArray &pemData, QByteArray *data) const; @@ -429,7 +418,7 @@ public: static void setNegotiatedProtocol(QSslSocketPrivate *d, const QByteArray &protocol); static void storePeerCertificate(QSslSocketPrivate *d, const QSslCertificate &peerCert); static void storePeerCertificateChain(QSslSocketPrivate *d, const QList &peerChain); - static void addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &rootCert); + static void addTustedRoot(QSslSocketPrivate *d, const QSslCertificate &rootCert);// TODO: "addTrusted..." // The next one - is a "very important" feature! Kidding ... static void setEphemeralKey(QSslSocketPrivate *d, const QSslKey &key); -- cgit v1.2.3