libQuotient
A Qt library for building matrix clients
keyverificationevent.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
2 // SPDX-License-Identifier: LGPL-2.1-or-later
3 
4 #pragma once
5 
6 #include "roomevent.h"
7 
8 namespace Quotient {
9 
10 constexpr inline auto SasV1Method = "m.sas.v1"_ls;
11 
12 class QUOTIENT_API KeyVerificationEvent : public RoomEvent {
13 public:
14  QUO_BASE_EVENT(KeyVerificationEvent, RoomEvent, "m.key.*")
15 
16  using RoomEvent::RoomEvent;
17 
18  /// An opaque identifier for the verification request. Must
19  /// be unique with respect to the devices involved.
20  QUO_CONTENT_GETTER(QString, transactionId)
21 };
22 
23 /// Requests a key verification with another user's devices.
24 /// Typically sent as a to-device event.
25 class QUOTIENT_API KeyVerificationRequestEvent : public KeyVerificationEvent {
26 public:
27  QUO_EVENT(KeyVerificationRequestEvent, "m.key.verification.request")
28 
29  using KeyVerificationEvent::KeyVerificationEvent;
30  KeyVerificationRequestEvent(const QString& transactionId,
31  const QString& fromDevice,
32  const QStringList& methods,
33  const QDateTime& timestamp)
34  : KeyVerificationRequestEvent(
35  basicJson(TypeId, { { "transaction_id"_ls, transactionId },
36  { "from_device"_ls, fromDevice },
37  { "methods"_ls, toJson(methods) },
38  { "timestamp"_ls, toJson(timestamp) } }))
39  {}
40 
41  /// The device ID which is initiating the request.
42  QUO_CONTENT_GETTER(QString, fromDevice)
43 
44  /// The verification methods supported by the sender.
45  QUO_CONTENT_GETTER(QStringList, methods)
46 
47  /// The POSIX timestamp in milliseconds for when the request was
48  /// made. If the request is in the future by more than 5 minutes or
49  /// more than 10 minutes in the past, the message should be ignored
50  /// by the receiver.
51  QUO_CONTENT_GETTER(QDateTime, timestamp)
52 };
53 
54 class QUOTIENT_API KeyVerificationReadyEvent : public KeyVerificationEvent {
55 public:
56  QUO_EVENT(KeyVerificationReadyEvent, "m.key.verification.ready")
57 
58  using KeyVerificationEvent::KeyVerificationEvent;
59  KeyVerificationReadyEvent(const QString& transactionId,
60  const QString& fromDevice,
61  const QStringList& methods)
62  : KeyVerificationReadyEvent(
63  basicJson(TypeId, { { "transaction_id"_ls, transactionId },
64  { "from_device"_ls, fromDevice },
65  { "methods"_ls, toJson(methods) } }))
66  {}
67 
68  /// The device ID which is accepting the request.
69  QUO_CONTENT_GETTER(QString, fromDevice)
70 
71  /// The verification methods supported by the sender.
72  QUO_CONTENT_GETTER(QStringList, methods)
73 };
74 
75 constexpr inline auto HmacSha256Code = "hkdf-hmac-sha256"_ls;
76 constexpr inline auto HmacSha256V2Code = "hkdf-hmac-sha256.v2"_ls;
77 constexpr std::array SupportedMacs { HmacSha256Code, HmacSha256V2Code };
78 
79 /// Begins a key verification process.
80 class QUOTIENT_API KeyVerificationStartEvent : public KeyVerificationEvent {
81 public:
82  QUO_EVENT(KeyVerificationStartEvent, "m.key.verification.start")
83 
84  using KeyVerificationEvent::KeyVerificationEvent;
85  KeyVerificationStartEvent(const QString& transactionId,
86  const QString& fromDevice)
87  : KeyVerificationStartEvent(
88  basicJson(TypeId, { { "transaction_id"_ls, transactionId },
89  { "from_device"_ls, fromDevice },
90  { "method"_ls, SasV1Method },
91  { "hashes"_ls, QJsonArray{ "sha256"_ls } },
92  { "key_agreement_protocols"_ls,
93  QJsonArray{ "curve25519-hkdf-sha256"_ls } },
94  { "message_authentication_codes"_ls,
95  toJson(SupportedMacs) },
96  { "short_authentication_string"_ls,
97  QJsonArray{ "decimal"_ls, "emoji"_ls } } }))
98  {}
99 
100  /// The device ID which is initiating the process.
101  QUO_CONTENT_GETTER(QString, fromDevice)
102 
103  /// The verification method to use.
104  QUO_CONTENT_GETTER(QString, method)
105 
106  /// Optional method to use to verify the other user's key with.
107  QUO_CONTENT_GETTER(std::optional<QString>, nextMethod)
108 
109  // SAS.V1 methods
110 
111  /// The key agreement protocols the sending device understands.
112  /// \note Only exist if method is m.sas.v1
113  QStringList keyAgreementProtocols() const
114  {
115  Q_ASSERT(method() == SasV1Method);
116  return contentPart<QStringList>("key_agreement_protocols"_ls);
117  }
118 
119  /// The hash methods the sending device understands.
120  /// \note Only exist if method is m.sas.v1
121  QStringList hashes() const
122  {
123  Q_ASSERT(method() == SasV1Method);
124  return contentPart<QStringList>("hashes"_ls);
125  }
126 
127  /// The message authentication codes that the sending device understands.
128  /// \note Only exist if method is m.sas.v1
129  QStringList messageAuthenticationCodes() const
130  {
131  Q_ASSERT(method() == SasV1Method);
132  return contentPart<QStringList>("message_authentication_codes"_ls);
133  }
134 
135  /// The SAS methods the sending device (and the sending device's
136  /// user) understands.
137  /// \note Only exist if method is m.sas.v1
138  QString shortAuthenticationString() const
139  {
140  Q_ASSERT(method() == SasV1Method);
141  return contentPart<QString>("short_authentication_string"_ls);
142  }
143 };
144 
145 /// Accepts a previously sent m.key.verification.start message.
146 /// Typically sent as a to-device event.
147 class QUOTIENT_API KeyVerificationAcceptEvent : public KeyVerificationEvent {
148 public:
149  QUO_EVENT(KeyVerificationAcceptEvent, "m.key.verification.accept")
150 
151  using KeyVerificationEvent::KeyVerificationEvent;
152  KeyVerificationAcceptEvent(const QString& transactionId,
153  const QString& commitment)
154  : KeyVerificationAcceptEvent(basicJson(
155  TypeId, { { "transaction_id"_ls, transactionId },
156  { "method"_ls, SasV1Method },
157  { "key_agreement_protocol"_ls, "curve25519-hkdf-sha256"_ls },
158  { "hash"_ls, "sha256"_ls },
159  { "message_authentication_code"_ls, HmacSha256V2Code },
160  { "short_authentication_string"_ls,
161  QJsonArray{ "decimal"_ls, "emoji"_ls, } },
162  { "commitment"_ls, commitment } }))
163  {}
164 
165  /// The verification method to use. Must be 'm.sas.v1'.
166  QUO_CONTENT_GETTER(QString, method)
167 
168  /// The key agreement protocol the device is choosing to use, out of
169  /// the options in the m.key.verification.start message.
170  QUO_CONTENT_GETTER(QString, keyAgreementProtocol)
171 
172  /// The hash method the device is choosing to use, out of the
173  /// options in the m.key.verification.start message.
174  QUO_CONTENT_GETTER_X(QString, hashData, "hash"_ls)
175 
176  /// The message authentication code the device is choosing to use, out
177  /// of the options in the m.key.verification.start message.
178  QUO_CONTENT_GETTER(QString, messageAuthenticationCode)
179 
180  /// The SAS methods both devices involved in the verification process understand.
181  QUO_CONTENT_GETTER(QStringList, shortAuthenticationString)
182 
183  /// The hash (encoded as unpadded base64) of the concatenation of the
184  /// device's ephemeral public key (encoded as unpadded base64) and the
185  /// canonical JSON representation of the m.key.verification.start message.
186  QUO_CONTENT_GETTER(QString, commitment)
187 };
188 
189 class QUOTIENT_API KeyVerificationCancelEvent : public KeyVerificationEvent {
190 public:
191  QUO_EVENT(KeyVerificationCancelEvent, "m.key.verification.cancel")
192 
193  using KeyVerificationEvent::KeyVerificationEvent;
194  KeyVerificationCancelEvent(const QString& transactionId,
195  const QString& reason)
196  : KeyVerificationCancelEvent(
197  basicJson(TypeId, {
198  { "transaction_id"_ls, transactionId },
199  { "reason"_ls, reason },
200  { "code"_ls, reason } // Not a typo
201  }))
202  {}
203 
204  /// A human readable description of the code. The client should only
205  /// rely on this string if it does not understand the code.
206  QUO_CONTENT_GETTER(QString, reason)
207 
208  /// The error code for why the process/request was cancelled by the user.
209  QUO_CONTENT_GETTER(QString, code)
210 };
211 
212 /// Sends the ephemeral public key for a device to the partner device.
213 /// Typically sent as a to-device event.
214 class QUOTIENT_API KeyVerificationKeyEvent : public KeyVerificationEvent {
215 public:
216  QUO_EVENT(KeyVerificationKeyEvent, "m.key.verification.key")
217 
218  using KeyVerificationEvent::KeyVerificationEvent;
219  KeyVerificationKeyEvent(const QString& transactionId, const QString& key)
220  : KeyVerificationKeyEvent(
221  basicJson(TypeId, { { "transaction_id"_ls, transactionId },
222  { "key"_ls, key } }))
223  {}
224 
225  /// The device's ephemeral public key, encoded as unpadded base64.
226  QUO_CONTENT_GETTER(QString, key)
227 };
228 
229 /// Sends the MAC of a device's key to the partner device.
230 class QUOTIENT_API KeyVerificationMacEvent : public KeyVerificationEvent {
231 public:
232  QUO_EVENT(KeyVerificationMacEvent, "m.key.verification.mac")
233 
234  using KeyVerificationEvent::KeyVerificationEvent;
235  KeyVerificationMacEvent(const QString& transactionId, const QString& keys,
236  const QJsonObject& mac)
237  : KeyVerificationMacEvent(
238  basicJson(TypeId, { { "transaction_id"_ls, transactionId },
239  { "keys"_ls, keys },
240  { "mac"_ls, mac } }))
241  {}
242 
243  /// The device's ephemeral public key, encoded as unpadded base64.
244  QUO_CONTENT_GETTER(QString, keys)
245 
246  QHash<QString, QString> mac() const
247  {
248  return contentPart<QHash<QString, QString>>("mac"_ls);
249  }
250 };
251 
252 class QUOTIENT_API KeyVerificationDoneEvent : public KeyVerificationEvent {
253 public:
254  QUO_EVENT(KeyVerificationDoneEvent, "m.key.verification.done")
255 
256  using KeyVerificationEvent::KeyVerificationEvent;
257  explicit KeyVerificationDoneEvent(const QString& transactionId)
258  : KeyVerificationDoneEvent(
259  basicJson(TypeId, { { "transaction_id"_ls, transactionId } }))
260  {}
261 };
262 } // namespace Quotient