// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qtlsbackend_p.h"

#if QT_CONFIG(ssl)
#include "qsslpresharedkeyauthenticator_p.h"
#include "qsslpresharedkeyauthenticator.h"
#include "qsslsocket_p.h"
#include "qsslcipher_p.h"
#include "qsslkey_p.h"
#include "qsslkey.h"
#endif

#include "qssl_p.h"

#include <QtCore/private/qfactoryloader_p.h>

#include "QtCore/qapplicationstatic.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmutex.h>

#include <algorithm>
#include <vector>

QT_BEGIN_NAMESPACE

using namespace Qt::StringLiterals;

Q_APPLICATION_STATIC(QFactoryLoader, loader, QTlsBackend_iid,
                     QStringLiteral("/tls"))

namespace {

class BackendCollection
{
public:
    void addBackend(QTlsBackend *backend)
    {
        Q_ASSERT(backend);
        Q_ASSERT(std::find(backends.begin(), backends.end(), backend) == backends.end());
        const QMutexLocker locker(&collectionMutex);
        backends.push_back(backend);
    }

    void removeBackend(QTlsBackend *backend)
    {
        Q_ASSERT(backend);
        const QMutexLocker locker(&collectionMutex);
        const auto it = std::find(backends.begin(), backends.end(), backend);
        Q_ASSERT(it != backends.end());
        backends.erase(it);
    }

    bool tryPopulateCollection()
    {
        if (!loader())
            return false;

        Q_CONSTINIT static QBasicMutex mutex;
        const QMutexLocker locker(&mutex);
        if (backends.size())
            return true;

#if QT_CONFIG(library)
        loader->update();
#endif
        int index = 0;
        while (loader->instance(index))
            ++index;

        return true;
    }

    QList<QString> backendNames()
    {
        QList<QString> names;
        if (!tryPopulateCollection())
            return names;

        const QMutexLocker locker(&collectionMutex);
        if (!backends.size())
            return names;

        names.reserve(backends.size());
        for (const auto *backend : backends) {
            if (backend->isValid())
                names.append(backend->backendName());
        }

        return names;
    }

    QTlsBackend *backend(const QString &name)
    {
        if (!tryPopulateCollection())
            return nullptr;

        const QMutexLocker locker(&collectionMutex);
        const auto it = std::find_if(backends.begin(), backends.end(),
                                     [&name](const auto *fct) {return fct->backendName() == name;});

        return it == backends.end()  ? nullptr : *it;
    }

private:
    std::vector<QTlsBackend *> backends;
    QMutex collectionMutex;
};

} // Unnamed namespace

Q_GLOBAL_STATIC(BackendCollection, backends);

/*!
    \class QTlsBackend
    \internal (Network-private)
    \brief QTlsBackend is a factory class, providing implementations
    for the QSsl classes.

    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.

    Inheriting from QTlsBackend and creating an object of such
    a class adds a new backend into the list of available TLS backends.

    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.

    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.

    QTlsBackend also provides some auxiliary functions that a derived
    backend class can use to interact with the internals of network-private classes.

    \sa QSslSocket::availableBackends(), supportedFeatures(), supportedProtocols(), implementedClasses()
*/

/*!
    \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".

    \sa QSslSocket::availableBackends(), isValid()
*/

const QString QTlsBackend::builtinBackendNames[] = {
    QStringLiteral("schannel"),
    QStringLiteral("securetransport"),
    QStringLiteral("openssl"),
    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);

    if (QCoreApplication::instance()) {
        connect(QCoreApplication::instance(), &QCoreApplication::destroyed, this, [this] {
            delete 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
{
}

#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<QSslCertificate> 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);
    Q_UNUSED(mode);
    REPORT_MISSING_SUPPORT("does not support QDtls");
    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<int> 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);
    REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
    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);
    REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
    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);
    REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
    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);
    REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
    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);
    REPORT_MISSING_SUPPORT("does not support QSslEllipticCurve");
    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);
    Q_UNUSED(data);
    REPORT_MISSING_SUPPORT("does not support QSslDiffieHellmanParameters in DER format");
    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);
    Q_UNUSED(data);
    REPORT_MISSING_SUPPORT("does not support QSslDiffieHellmanParameters in PEM format");
    return {};
}

/*!
    \internal
    Returns a list of names of available backends.

    \note This list contains only properly initialized backends.

    \sa QTlsBackend(), isValid()
*/
QList<QString> QTlsBackend::availableBackendNames()
{
    if (!backends())
        return {};

    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:
    const auto names = availableBackendNames();
    auto name = builtinBackendNames[nameIndexOpenSSL];
    if (names.contains(name))
        return name;
    name = builtinBackendNames[nameIndexSchannel];
    if (names.contains(name))
        return name;
    name = builtinBackendNames[nameIndexSecureTransport];
    if (names.contains(name))
        return name;

    const auto pos = std::find_if(names.begin(), names.end(), [](const auto &name) {
        return name != builtinBackendNames[nameIndexCertOnly];
    });

    if (pos != names.end())
        return *pos;

    if (names.size())
        return names[0];

    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())
        return {};

    if (auto *fct = backends->backend(backendName))
        return fct;

    qCWarning(lcSsl) << "Cannot create unknown backend named" << 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)
    return QSslSocketPrivate::tlsBackendInUse();
#else
    return findBackend(defaultBackendName());
#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<QSsl::SslProtocol> QTlsBackend::supportedProtocols(const QString &backendName)
{
    if (!backends())
        return {};

    if (const auto *fct = backends->backend(backendName))
        return fct->supportedProtocols();

    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<QSsl::SupportedFeature> QTlsBackend::supportedFeatures(const QString &backendName)
{
    if (!backends())
        return {};

    if (const auto *fct = backends->backend(backendName))
        return fct->supportedFeatures();

    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<QSsl::ImplementedClass> QTlsBackend::implementedClasses(const QString &backendName)
{
    if (!backends())
        return {};

    if (const auto *fct = backends->backend(backendName))
        return fct->implementedClasses();

    return {};
}

/*!
    \internal
    Auxiliary function. Initializes \a key to use \a keyBackend.
*/
void QTlsBackend::resetBackend(QSslKey &key, QTlsPrivate::TlsKey *keyBackend)
{
#if QT_CONFIG(ssl)
    key.d->backend.reset(keyBackend);
#else
    Q_UNUSED(key);
    Q_UNUSED(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)
{
    Q_ASSERT(auth);
#if QT_CONFIG(ssl)
    if (hint)
        auth->d->identityHint = QByteArray::fromRawData(hint, hintLength); // it's NUL terminated, but do not include the NUL

    auth->d->maximumIdentityLength = int(maxIdentityLen) - 1; // needs to be NUL terminated
    auth->d->maximumPreSharedKeyLength = int(maxPskLen);
#else
    Q_UNUSED(auth);
    Q_UNUSED(hint);
    Q_UNUSED(hintLength);
    Q_UNUSED(maxIdentityLen);
    Q_UNUSED(maxPskLen);
#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)
{
#if QT_CONFIG(ssl)
    Q_ASSERT(auth);
    auth->d->identityHint = identityHint;
    auth->d->identity = identity;
    auth->d->maximumIdentityLength = 0; // user cannot set an identity
    auth->d->maximumPreSharedKeyLength = int(maxPskLen);
#else
    Q_UNUSED(auth);
    Q_UNUSED(identity);
    Q_UNUSED(identityHint);
    Q_UNUSED(maxPskLen);
#endif
}

#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;

    const auto descriptionList = QStringView{descriptionOneLine}.split(u' ', Qt::SkipEmptyParts);
    if (descriptionList.size() > 5) {
        ciph.d->isNull = false;
        ciph.d->name = descriptionList.at(0).toString();

        QStringView protoString = descriptionList.at(1);
        ciph.d->protocolString = protoString.toString();
        ciph.d->protocol = QSsl::UnknownProtocol;
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
        if (protoString.startsWith(u"TLSv1")) {
            QStringView tail = protoString.sliced(5);
            if (tail.startsWith(u'.')) {
                tail = tail.sliced(1);
                if (tail == u"3")
                    ciph.d->protocol = QSsl::TlsV1_3;
                else if (tail == u"2")
                    ciph.d->protocol = QSsl::TlsV1_2;
                else if (tail == u"1")
                    ciph.d->protocol = QSsl::TlsV1_1;
            } else if (tail.isEmpty()) {
                ciph.d->protocol = QSsl::TlsV1_0;
            }
        }
QT_WARNING_POP

        if (descriptionList.at(2).startsWith("Kx="_L1))
            ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3).toString();
        if (descriptionList.at(3).startsWith("Au="_L1))
            ciph.d->authenticationMethod = descriptionList.at(3).mid(3).toString();
        if (descriptionList.at(4).startsWith("Enc="_L1))
            ciph.d->encryptionMethod = descriptionList.at(4).mid(4).toString();
        ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == "export"_L1);

        ciph.d->bits = bits;
        ciph.d->supportedBits = supportedBits;
    }

    return ciph;
}

/*!
    \internal
    Auxiliary function. Creates a new QSslCipher from \a suiteName, \a protocol version and
    \a protocolString. For example:
    \code
    createCiphersuite("ECDHE-RSA-AES256-GCM-SHA256"_L1, QSsl::TlsV1_2, "TLSv1.2"_L1);
    \endcode
*/
QSslCipher QTlsBackend::createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol,
                                          const QString &protocolString)
{
    QSslCipher ciph;

    if (!suiteName.size())
        return ciph;

    ciph.d->isNull = false;
    ciph.d->name = suiteName;
    ciph.d->protocol = protocol;
    ciph.d->protocolString = protocolString;

    const auto bits = QStringView{ciph.d->name}.split(u'-');
    if (bits.size() >= 2) {
        if (bits.size() == 2 || bits.size() == 3)
            ciph.d->keyExchangeMethod = "RSA"_L1;
        else if (bits.front() == "DH"_L1 || bits.front() == "DHE"_L1)
            ciph.d->keyExchangeMethod = "DH"_L1;
        else if (bits.front() == "ECDH"_L1 || bits.front() == "ECDHE"_L1)
            ciph.d->keyExchangeMethod = "ECDH"_L1;
        else
            qCWarning(lcSsl) << "Unknown Kx" << ciph.d->name;

        if (bits.size() == 2 || bits.size() == 3)
            ciph.d->authenticationMethod = "RSA"_L1;
        else if (ciph.d->name.contains("-ECDSA-"_L1))
            ciph.d->authenticationMethod = "ECDSA"_L1;
        else if (ciph.d->name.contains("-RSA-"_L1))
            ciph.d->authenticationMethod = "RSA"_L1;
        else
            qCWarning(lcSsl) << "Unknown Au" << ciph.d->name;

        if (ciph.d->name.contains("RC4-"_L1)) {
            ciph.d->encryptionMethod = "RC4(128)"_L1;
            ciph.d->bits = 128;
            ciph.d->supportedBits = 128;
        } else if (ciph.d->name.contains("DES-CBC3-"_L1)) {
            ciph.d->encryptionMethod = "3DES(168)"_L1;
            ciph.d->bits = 168;
            ciph.d->supportedBits = 168;
        } else if (ciph.d->name.contains("AES128-"_L1)) {
            ciph.d->encryptionMethod = "AES(128)"_L1;
            ciph.d->bits = 128;
            ciph.d->supportedBits = 128;
        } else if (ciph.d->name.contains("AES256-GCM"_L1)) {
            ciph.d->encryptionMethod = "AESGCM(256)"_L1;
            ciph.d->bits = 256;
            ciph.d->supportedBits = 256;
        } else if (ciph.d->name.contains("AES256-"_L1)) {
            ciph.d->encryptionMethod = "AES(256)"_L1;
            ciph.d->bits = 256;
            ciph.d->supportedBits = 256;
        } else if (ciph.d->name.contains("CHACHA20-"_L1)) {
            ciph.d->encryptionMethod = "CHACHA20"_L1;
            ciph.d->bits = 256;
            ciph.d->supportedBits = 256;
        } else if (ciph.d->name.contains("NULL-"_L1)) {
            ciph.d->encryptionMethod = "NULL"_L1;
        } else {
            qCWarning(lcSsl) << "Unknown Enc" << ciph.d->name;
        }
    }
    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, "TLSv1.2"_L1);
    \endcode
*/
QSslCipher QTlsBackend::createCipher(const QString &name, QSsl::SslProtocol protocol,
                                     const QString &protocolString)
{
    // Note the name 'createCipher' (not 'ciphersuite'): we don't provide
    // information about Kx, Au, bits/supported etc.
    QSslCipher cipher;
    cipher.d->isNull = false;
    cipher.d->name = name;
    cipher.d->protocol = protocol;
    cipher.d->protocolString = protocolString;
    return cipher;
}

/*!
    \internal
    Returns an implementation-specific list of ciphersuites that can be used by QSslSocket.

    \sa QSslConfiguration::defaultCiphers()
*/
QList<QSslCipher> QTlsBackend::defaultCiphers()
{
    return QSslSocketPrivate::defaultCiphers();
}

/*!
    \internal
    Returns an implementation-specific list of ciphersuites that can be used by QDtls.
*/
QList<QSslCipher> QTlsBackend::defaultDtlsCiphers()
{
    return QSslSocketPrivate::defaultDtlsCiphers();
}

/*!
    \internal
    Sets \a ciphers as defaults ciphers that QSslSocket can use.

    \sa defaultCiphers()
*/
void QTlsBackend::setDefaultCiphers(const QList<QSslCipher> &ciphers)
{
    QSslSocketPrivate::setDefaultCiphers(ciphers);
}

/*!
    \internal
    Sets \a ciphers as defaults ciphers that QDtls can use.

    \sa defaultDtlsCiphers()
*/
void QTlsBackend::setDefaultDtlsCiphers(const QList<QSslCipher> &ciphers)
{
    QSslSocketPrivate::setDefaultDtlsCiphers(ciphers);
}

/*!
    \internal
    Sets \a ciphers as a list of supported ciphers.

    \sa QSslConfiguration::supportedCiphers()
*/
void QTlsBackend::setDefaultSupportedCiphers(const QList<QSslCipher> &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<QSslCertificate> &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<QSslCertificate> &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);
    d->configuration.peerCertificate.clear();
    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<QSslCertificate> &peerChain)
{
    Q_ASSERT(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);
    if (!d->configuration.caCertificates.contains(rootCert))
        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<QByteArray, QByteArray> *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<QByteArray, QByteArray> &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
*/

/*!
    \fn QByteArray TlsKey::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &passPhrase, const QByteArray &iv) const
    \internal

    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 expected 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<QByteArray> 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<QByteArray> 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<QSsl::AlternativeNameEntryType, QString> 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 plugin 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<QSslError> 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::encrypted()), 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(std::shared_ptr<QSslContext> 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()
*/
std::shared_ptr<QSslContext> 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<QOcspResponse> 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 common 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 &params)
    \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 {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<QSslError> 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<QSslError> &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())
        backend->forceAutotestSecurityLevel();
}

#endif // QT_CONFIG(ssl)

QT_END_NAMESPACE

#include "moc_qtlsbackend_p.cpp"
