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