aboutsummaryrefslogtreecommitdiff
path: root/lib/keyverificationsession.h
blob: 075ea1e2d4717789257cb7a1b77aa29eec7d7425 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.1-or-later

#pragma once

#include "events/keyverificationevent.h"

#include <QtCore/QObject>

struct OlmSAS;

namespace Quotient {
class Connection;

struct QUOTIENT_API EmojiEntry {
    QString emoji;
    QString description;

    Q_GADGET
    Q_PROPERTY(QString emoji MEMBER emoji CONSTANT)
    Q_PROPERTY(QString description MEMBER description CONSTANT)
};

/** A key verification session. Listen for incoming sessions by connecting to Connection::newKeyVerificationSession.
    Start a new session using Connection::startKeyVerificationSession.
    The object is delete after finished is emitted.
*/
class QUOTIENT_API KeyVerificationSession : public QObject
{
    Q_OBJECT

public:
    enum State {
        INCOMING, ///< There is a request for verification incoming
        //! We sent a request for verification and are waiting for ready
        WAITINGFORREADY,
        //! Either party sent a ready as a response to a request; the user
        //! selects a method
        READY,
        WAITINGFORACCEPT, ///< We sent a start and are waiting for an accept
        ACCEPTED, ///< The other party sent an accept and is waiting for a key
        WAITINGFORKEY, ///< We're waiting for a key
        //! We're waiting for the *user* to verify the emojis
        WAITINGFORVERIFICATION,
        WAITINGFORMAC, ///< We're waiting for the mac
        CANCELED, ///< The session has been canceled
        DONE, ///< The verification is done
    };
    Q_ENUM(State)

    enum Error {
        NONE,
        TIMEOUT,
        REMOTE_TIMEOUT,
        USER,
        REMOTE_USER,
        UNEXPECTED_MESSAGE,
        REMOTE_UNEXPECTED_MESSAGE,
        UNKNOWN_TRANSACTION,
        REMOTE_UNKNOWN_TRANSACTION,
        UNKNOWN_METHOD,
        REMOTE_UNKNOWN_METHOD,
        KEY_MISMATCH,
        REMOTE_KEY_MISMATCH,
        USER_MISMATCH,
        REMOTE_USER_MISMATCH,
        INVALID_MESSAGE,
        REMOTE_INVALID_MESSAGE,
        SESSION_ACCEPTED,
        REMOTE_SESSION_ACCEPTED,
        MISMATCHED_COMMITMENT,
        REMOTE_MISMATCHED_COMMITMENT,
        MISMATCHED_SAS,
        REMOTE_MISMATCHED_SAS,
    };
    Q_ENUM(Error)

    Q_PROPERTY(QString remoteDeviceId MEMBER m_remoteDeviceId CONSTANT)
    Q_PROPERTY(QVector<EmojiEntry> sasEmojis READ sasEmojis NOTIFY sasEmojisChanged)
    Q_PROPERTY(State state READ state NOTIFY stateChanged)
    Q_PROPERTY(Error error READ error NOTIFY errorChanged)

    KeyVerificationSession(QString remoteUserId,
                           const KeyVerificationRequestEvent& event,
                           Connection* connection, bool encrypted);
    KeyVerificationSession(QString userId, QString deviceId,
                           Connection* connection);
    ~KeyVerificationSession() override;
    Q_DISABLE_COPY_MOVE(KeyVerificationSession)

    QVector<EmojiEntry> sasEmojis() const;
    State state() const;

    Error error() const;

public Q_SLOTS:
    void sendRequest();
    void sendReady();
    void sendMac();
    void sendStartSas();
    void sendKey();
    void sendDone();
    void cancelVerification(Error error);

Q_SIGNALS:
    void startReceived();
    void keyReceived();
    void sasEmojisChanged();
    void stateChanged();
    void errorChanged();
    void finished();

private:
    const QString m_remoteUserId;
    const QString m_remoteDeviceId;
    const QString m_transactionId;
    Connection* m_connection;
    OlmSAS* m_sas = nullptr;
    QVector<EmojiEntry> m_sasEmojis;
    bool startSentByUs = false;
    State m_state = INCOMING;
    Error m_error = NONE;
    QString m_startEvent;
    QString m_commitment;
    bool macReceived = false;
    bool m_encrypted;
    QStringList m_remoteSupportedMethods;
    bool m_verified = false;
    QString m_pendingEdKeyId{};

    void handleReady(const KeyVerificationReadyEvent& event);
    void handleStart(const KeyVerificationStartEvent& event);
    void handleAccept(const KeyVerificationAcceptEvent& event);
    void handleKey(const KeyVerificationKeyEvent& event);
    void handleMac(const KeyVerificationMacEvent& event);
    void handleDone(const KeyVerificationDoneEvent&);
    void handleCancel(const KeyVerificationCancelEvent& event);
    void init(std::chrono::milliseconds timeout);
    void setState(State state);
    void setError(Error error);
    static QString errorToString(Error error);
    static Error stringToError(const QString& error);
    void trustKeys();

    QByteArray macInfo(bool verifying, const QString& key = "KEY_IDS"_ls);
    QString calculateMac(const QString& input, bool verifying, const QString& keyId= "KEY_IDS"_ls);
};

} // namespace Quotient
Q_DECLARE_METATYPE(Quotient::EmojiEntry)
String& deviceId = {}, const QString& initialDeviceDisplayName = {}, Omittable<bool> inhibitLogin = none); ~RegisterJob() override; // Result properties /// The fully-qualified Matrix user ID (MXID) that has been registered. /// /// Any user ID returned by this API must conform to the grammar given in the /// `Matrix specification <https://matrix.org/docs/spec/appendices.html#user-identifiers>`_. const QString& userId() const; /// An access token for the account. /// This access token can then be used to authorize other requests. /// Required if the ``inhibit_login`` option is false. const QString& accessToken() const; /// The server_name of the homeserver on which the account has /// been registered. /// /// **Deprecated**. Clients should extract the server_name from /// ``user_id`` (by splitting at the first colon) if they require /// it. Note also that ``homeserver`` is not spelt this way. const QString& homeServer() const; /// ID of the registered device. Will be the same as the /// corresponding parameter in the request, if one was specified. /// Required if the ``inhibit_login`` option is false. const QString& deviceId() const; protected: Status parseJson(const QJsonDocument& data) override; private: class Private; QScopedPointer<Private> d; }; /// Begins the validation process for an email to be used during registration. /// /// Proxies the Identity Service API ``validate/email/requestToken``, but /// first checks that the given email address is not already associated /// with an account on this homeserver. See the Identity Service API for /// further information. class RequestTokenToRegisterEmailJob : public BaseJob { public: /*! Begins the validation process for an email to be used during registration. * \param clientSecret * A unique string generated by the client, and used to identify the * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param email * The email address to validate. * \param sendAttempt * The server will only send an email if the ``send_attempt`` * is a number greater than the most recent one which it has seen, * scoped to that ``email`` + ``client_secret`` pair. This is to * avoid repeatedly sending the same email in the case of request * retries between the POSTing user and the identity server. * The client should increment this value if they desire a new * email (e.g. a reminder) to be sent. * \param idServer * The hostname of the identity server to communicate with. May * optionally include a port. * \param nextLink * Optional. When the validation is completed, the identity * server will redirect the user to this URL. */ explicit RequestTokenToRegisterEmailJob(const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink = {}); ~RequestTokenToRegisterEmailJob() override; // Result properties /// An email has been sent to the specified address. /// Note that this may be an email containing the validation token or it may be informing /// the user of an error. const Sid& data() const; protected: Status parseJson(const QJsonDocument& data) override; private: class Private; QScopedPointer<Private> d; }; /// Requests a validation token be sent to the given phone number for the purpose of registering an account /// /// Proxies the Identity Service API ``validate/msisdn/requestToken``, but /// first checks that the given phone number is not already associated /// with an account on this homeserver. See the Identity Service API for /// further information. class RequestTokenToRegisterMSISDNJob : public BaseJob { public: /*! Requests a validation token be sent to the given phone number for the purpose of registering an account * \param clientSecret * A unique string generated by the client, and used to identify the * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param country * The two-letter uppercase ISO country code that the number in * ``phone_number`` should be parsed as if it were dialled from. * \param phoneNumber * The phone number to validate. * \param sendAttempt * The server will only send an SMS if the ``send_attempt`` is a * number greater than the most recent one which it has seen, * scoped to that ``country`` + ``phone_number`` + ``client_secret`` * triple. This is to avoid repeatedly sending the same SMS in * the case of request retries between the POSTing user and the * identity server. The client should increment this value if * they desire a new SMS (e.g. a reminder) to be sent. * \param idServer * The hostname of the identity server to communicate with. May * optionally include a port. * \param nextLink * Optional. When the validation is completed, the identity * server will redirect the user to this URL. */ explicit RequestTokenToRegisterMSISDNJob(const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink = {}); ~RequestTokenToRegisterMSISDNJob() override; // Result properties /// An SMS message has been sent to the specified phone number. /// Note that this may be an SMS message containing the validation token or it may be informing /// the user of an error. const Sid& data() const; protected: Status parseJson(const QJsonDocument& data) override; private: class Private; QScopedPointer<Private> d; }; /// Changes a user's password. /// /// Changes the password for an account on this homeserver. /// /// This API endpoint uses the `User-Interactive Authentication API`_. /// /// An access token should be submitted to this endpoint if the client has /// an active session. /// /// The homeserver may change the flows available depending on whether a /// valid access token is provided. class ChangePasswordJob : public BaseJob { public: /*! Changes a user's password. * \param newPassword * The new password for the account. * \param auth * Additional authentication information for the user-interactive authentication API. */ explicit ChangePasswordJob(const QString& newPassword, const Omittable<AuthenticationData>& auth = none); }; /// Requests a validation token be sent to the given email address for the purpose of resetting a user's password /// /// Proxies the Identity Service API ``validate/email/requestToken``, but /// first checks that the given email address **is** associated with an account /// on this homeserver. This API should be used to request /// validation tokens when authenticating for the /// `account/password` endpoint. This API's parameters and response are /// identical to that of the HS API |/register/email/requestToken|_ except that /// `M_THREEPID_NOT_FOUND` may be returned if no account matching the /// given email address could be found. The server may instead send an /// email to the given address prompting the user to create an account. /// `M_THREEPID_IN_USE` may not be returned. /// /// .. |/register/email/requestToken| replace:: ``/register/email/requestToken`` /// /// .. _/register/email/requestToken: #post-matrix-client-r0-register-email-requesttoken class RequestTokenToResetPasswordEmailJob : public BaseJob { public: /*! Requests a validation token be sent to the given email address for the purpose of resetting a user's password * \param clientSecret * A unique string generated by the client, and used to identify the * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param email * The email address to validate. * \param sendAttempt * The server will only send an email if the ``send_attempt`` * is a number greater than the most recent one which it has seen, * scoped to that ``email`` + ``client_secret`` pair. This is to * avoid repeatedly sending the same email in the case of request * retries between the POSTing user and the identity server. * The client should increment this value if they desire a new * email (e.g. a reminder) to be sent. * \param idServer * The hostname of the identity server to communicate with. May * optionally include a port. * \param nextLink * Optional. When the validation is completed, the identity * server will redirect the user to this URL. */ explicit RequestTokenToResetPasswordEmailJob(const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink = {}); ~RequestTokenToResetPasswordEmailJob() override; // Result properties /// An email was sent to the given address. const Sid& data() const; protected: Status parseJson(const QJsonDocument& data) override; private: class Private; QScopedPointer<Private> d; }; /// Requests a validation token be sent to the given phone number for the purpose of resetting a user's password. /// /// Proxies the Identity Service API ``validate/msisdn/requestToken``, but /// first checks that the given phone number **is** associated with an account /// on this homeserver. This API should be used to request /// validation tokens when authenticating for the /// `account/password` endpoint. This API's parameters and response are /// identical to that of the HS API |/register/msisdn/requestToken|_ except that /// `M_THREEPID_NOT_FOUND` may be returned if no account matching the /// given phone number could be found. The server may instead send an /// SMS message to the given address prompting the user to create an account. /// `M_THREEPID_IN_USE` may not be returned. /// /// .. |/register/msisdn/requestToken| replace:: ``/register/msisdn/requestToken`` /// /// .. _/register/msisdn/requestToken: #post-matrix-client-r0-register-email-requesttoken class RequestTokenToResetPasswordMSISDNJob : public BaseJob { public: /*! Requests a validation token be sent to the given phone number for the purpose of resetting a user's password. * \param clientSecret * A unique string generated by the client, and used to identify the * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param country * The two-letter uppercase ISO country code that the number in * ``phone_number`` should be parsed as if it were dialled from. * \param phoneNumber * The phone number to validate. * \param sendAttempt * The server will only send an SMS if the ``send_attempt`` is a * number greater than the most recent one which it has seen, * scoped to that ``country`` + ``phone_number`` + ``client_secret`` * triple. This is to avoid repeatedly sending the same SMS in * the case of request retries between the POSTing user and the * identity server. The client should increment this value if * they desire a new SMS (e.g. a reminder) to be sent. * \param idServer * The hostname of the identity server to communicate with. May * optionally include a port. * \param nextLink * Optional. When the validation is completed, the identity * server will redirect the user to this URL. */ explicit RequestTokenToResetPasswordMSISDNJob(const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink = {}); ~RequestTokenToResetPasswordMSISDNJob() override; // Result properties /// An SMS message was sent to the given phone number. const Sid& data() const; protected: Status parseJson(const QJsonDocument& data) override; private: class Private; QScopedPointer<Private> d; }; /// Deactivate a user's account. /// /// Deactivate the user's account, removing all ability for the user to /// login again. /// /// This API endpoint uses the `User-Interactive Authentication API`_. /// /// An access token should be submitted to this endpoint if the client has /// an active session. /// /// The homeserver may change the flows available depending on whether a /// valid access token is provided. class DeactivateAccountJob : public BaseJob { public: /*! Deactivate a user's account. * \param auth * Additional authentication information for the user-interactive authentication API. */ explicit DeactivateAccountJob(const Omittable<AuthenticationData>& auth = none); }; /// Checks to see if a username is available on the server. /// /// Checks to see if a username is available, and valid, for the server. /// /// The server should check to ensure that, at the time of the request, the /// username requested is available for use. This includes verifying that an /// application service has not claimed the username and that the username /// fits the server's desired requirements (for example, a server could dictate /// that it does not permit usernames with underscores). /// /// Matrix clients may wish to use this API prior to attempting registration, /// however the clients must also be aware that using this API does not normally /// reserve the username. This can mean that the username becomes unavailable /// between checking its availability and attempting to register it. class CheckUsernameAvailabilityJob : public BaseJob { public: /*! Checks to see if a username is available on the server. * \param username * The username to check the availability of. */ explicit CheckUsernameAvailabilityJob(const QString& username); /*! Construct a URL without creating a full-fledged job object * * This function can be used when a URL for * CheckUsernameAvailabilityJob is necessary but the job * itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& username); ~CheckUsernameAvailabilityJob() override; // Result properties /// A flag to indicate that the username is available. This should always /// be ``true`` when the server replies with 200 OK. Omittable<bool> available() const; protected: Status parseJson(const QJsonDocument& data) override; private: class Private; QScopedPointer<Private> d; }; } // namespace QMatrixClient