QDtlsClientVerifier

This class implements server-side DTLS cookie generation and verification. More

Inheritance diagram of PySide6.QtNetwork.QDtlsClientVerifier

Synopsis

Functions

Detailed Description

The QDtlsClientVerifier class implements server-side DTLS cookie generation and verification. Datagram security protocols are highly susceptible to a variety of Denial-of-Service attacks. According to RFC 6347, section 4.2.1, these are two of the more common types of attack:

  • An attacker transmits a series of handshake initiation requests, causing a server to allocate excessive resources and potentially perform expensive cryptographic operations.

  • An attacker transmits a series of handshake initiation requests with a forged source of the victim, making the server act as an amplifier. Normally, the server would reply to the victim machine with a Certificate message, which can be quite large, thus flooding the victim machine with datagrams.

As a countermeasure to these attacks, RFC 6347, section 4.2.1 proposes a stateless cookie technique that a server may deploy:

  • In response to the initial ClientHello message, the server sends a HelloVerifyRequest, which contains a cookie. This cookie is a cryptographic hash and is generated using the client’s address, port number, and the server’s secret (which is a cryptographically strong pseudo-random sequence of bytes).

  • A reachable DTLS client is expected to reply with a new ClientHello message containing this cookie.

  • When the server receives the ClientHello message with a cookie, it generates a new cookie as described above. This new cookie is compared to the one found in the ClientHello message.

  • In the cookies are equal, the client is considered to be real, and the server can continue with a TLS handshake procedure.

Note

A DTLS server is not required to use DTLS cookies.

QDtlsClientVerifier is designed to work in pair with QUdpSocket , as shown in the following code-excerpt:

class DtlsServer(QObject):

# public
    listen = bool(QHostAddress address, quint16 port)
    # ...
# private
    def readyRead():
    # ...
    serverSocket = QUdpSocket()
    verifier = QDtlsClientVerifier()
    # ...

def listen(self, QHostAddress serverAddress, quint16 serverPort):

    if (serverSocket.bind(serverAddress, serverPort))
        connect(serverSocket, QUdpSocket.readyRead, self, DtlsServer.readyRead)
    return serverSocket.state() == QAbstractSocket.BoundState

def readyRead(self):

    dgram = QByteArray(serverSocket.pendingDatagramSize(), Qt.Uninitialized)
    address = QHostAddress()
    port = {}
    serverSocket.readDatagram(dgram.data(), dgram.size(), address, port)
    if (verifiedClients.contains({address, port}) {
        # This client was verified previously, we either continue the
        # handshake or decrypt the incoming message.
    } else if (verifier.verifyClient(serverSocket, dgram, address, port)) {
        # Apparently we have a real DTLS client who wants to send us
        # encrypted datagrams. Remember this client as verified
        # and proceed with a handshake.
    else:
        # No matching cookie was found in the incoming datagram,
        # verifyClient() has sent a ClientVerify message.
        # We'll hear from the client again soon, if they're real.

QDtlsClientVerifier does not impose any restrictions on how the application uses QUdpSocket . For example, it is possible to have a server with a single QUdpSocket in state BoundState , handling multiple DTLS clients simultaneously:

  • Testing if new clients are real DTLS-capable clients.

  • Completing TLS handshakes with the verified clients (see QDtls ).

  • Decrypting datagrams coming from the connected clients (see QDtls ).

  • Sending encrypted datagrams to the connected clients (see QDtls ).

This implies that QDtlsClientVerifier does not read directly from a socket, instead it expects the application to read an incoming datagram, extract the sender’s address, and port, and then pass this data to verifyClient() . To send a HelloVerifyRequest message, verifyClient() can write to the QUdpSocket .

Note

QDtlsClientVerifier does not take ownership of the QUdpSocket object.

By default QDtlsClientVerifier obtains its secret from a cryptographically strong pseudorandom number generator.

Note

The default secret is shared by all objects of the classes QDtlsClientVerifier and QDtls . Since this can impose security risks, RFC 6347 recommends to change the server’s secret frequently. Please see RFC 6347, section 4.2.1 for hints about possible server implementations. Cookie generator parameters can be set using the class GeneratorParameters and setCookieGeneratorParameters() :

def updateServerSecret(self):

    newSecret = QByteArray(generateCryptoStrongSecret())
    if (newSecret.size()) {
        usedCookies.append(newSecret)
        verifier.setCookieGeneratorParameters({QCryptographicHash.Sha1, newSecret})

The DTLS server example illustrates how to use QDtlsClientVerifier in a server application.

See also

QUdpSocket BoundState QDtls verifyClient() GeneratorParameters setCookieGeneratorParameters() cookieGeneratorParameters() setCookieGeneratorParameters() cookieGeneratorParameters() Algorithm QDtlsError dtlsError() dtlsErrorString()

class PySide6.QtNetwork.QDtlsClientVerifier([parent=None])
Parameters

parentPySide6.QtCore.QObject

Constructs a QDtlsClientVerifier object, parent is passed to QObject ‘s constructor.

PySide6.QtNetwork.QDtlsClientVerifier.dtlsError()
Return type

QDtlsError

Returns the last error that occurred or NoError .

See also

QDtlsError dtlsErrorString()

PySide6.QtNetwork.QDtlsClientVerifier.dtlsErrorString()
Return type

str

Returns a textual description of the last error, or an empty string.

See also

dtlsError()

PySide6.QtNetwork.QDtlsClientVerifier.verifiedHello()
Return type

PySide6.QtCore.QByteArray

Convenience function. Returns the last ClientHello message that was successfully verified, or an empty QByteArray if no verification has completed.

See also

verifyClient()

PySide6.QtNetwork.QDtlsClientVerifier.verifyClient(socket, dgram, address, port)
Parameters
Return type

bool

socket must be a valid pointer, dgram must be a non-empty datagram, address cannot be null, broadcast, or multicast. port is the remote peer’s port. This function returns true if dgram contains a ClientHello message with a valid cookie. If no matching cookie is found, will send a HelloVerifyRequest message using socket and return false.

The following snippet shows how a server application may check for errors:

if not verifier.verifyClient(socket, message, address, port):
    switch (verifyClient.dtlsError()) {
    QDtlsError.NoError: = case()
        # Not verified yet, but no errors found and we have to wait for the next
        # message from this client.
        return
    QDtlsError.TlsInitializationError: = case()
        # This error is fatal, nothing we can do about it.
        # Probably, quit the server after reporting the error.
        return
    QDtlsError.UnderlyingSocketError: = case()
        # There is some problem in QUdpSocket, handle it (see QUdpSocket::error())
        return
    QDtlsError.InvalidInputParameters: = case()
    default:
        Q_UNREACHABLE()

See also

isNull() isBroadcast() isMulticast() setCookieGeneratorParameters() cookieGeneratorParameters()