matrix_sdk/encryption/identities/users.rs
1// Copyright 2021 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::BTreeMap;
16
17use matrix_sdk_base::{
18 RoomMemberships,
19 crypto::{CryptoStoreError, UserIdentity as CryptoUserIdentity, types::MasterPubkey},
20};
21use ruma::{
22 OwnedUserId, UserId,
23 events::{key::verification::VerificationMethod, room::message::RoomMessageEventContent},
24};
25
26use super::{ManualVerifyError, RequestVerificationError};
27use crate::{Client, encryption::verification::VerificationRequest};
28
29/// Updates about [`UserIdentity`]s which got received over the `/keys/query`
30/// endpoint.
31#[derive(Clone, Debug, Default)]
32pub struct IdentityUpdates {
33 /// The list of newly discovered user identities .
34 ///
35 /// A identity being in this list does not necessarily mean that the
36 /// identity was just created, it just means that it's the first time we're
37 /// seeing this identity.
38 pub new: BTreeMap<OwnedUserId, UserIdentity>,
39 /// The list of changed identities.
40 pub changed: BTreeMap<OwnedUserId, UserIdentity>,
41}
42
43impl IdentityUpdates {
44 pub(crate) fn new(
45 client: Client,
46 updates: matrix_sdk_base::crypto::store::types::IdentityUpdates,
47 ) -> Self {
48 let new = updates
49 .new
50 .into_iter()
51 .map(|(user_id, identity)| (user_id, UserIdentity::new(client.to_owned(), identity)))
52 .collect();
53
54 let changed = updates
55 .changed
56 .into_iter()
57 .map(|(user_id, identity)| (user_id, UserIdentity::new(client.to_owned(), identity)))
58 .collect();
59
60 Self { new, changed }
61 }
62}
63
64/// A struct representing a E2EE capable identity of a user.
65///
66/// The identity is backed by public [cross signing] keys that users upload. If
67/// our own user doesn't yet have such an identity, a new one can be created and
68/// uploaded to the server using [`Encryption::bootstrap_cross_signing()`]. The
69/// user identity can be also reset using the same method.
70///
71/// The user identity consists of three separate `Ed25519` keypairs:
72///
73/// ```text
74/// ┌──────────────────────────────────────────────────────┐
75/// │ User Identity │
76/// ├────────────────┬──────────────────┬──────────────────┤
77/// │ Master Key │ Self-signing Key │ User-signing key │
78/// └────────────────┴──────────────────┴──────────────────┘
79/// ```
80///
81/// The identity consists of a Master key and two sub-keys, the Self-signing key
82/// and the User-signing key.
83///
84/// Each key has a separate role:
85///
86/// - Master key, signs only the sub-keys, can be used as a fingerprint of the
87/// identity.
88/// - Self-signing key, signs devices belonging to the user that owns this
89/// identity.
90/// - User-signing key, signs Master keys belonging to other users.
91///
92/// The User-signing key and its signatures of other user's Master keys are
93/// hidden from us by the homeserver. This is done to preserve privacy and not
94/// let us know whom the user verified.
95///
96/// [cross signing]: https://spec.matrix.org/unstable/client-server-api/#cross-signing
97/// [`Encryption::bootstrap_cross_signing()`]: crate::encryption::Encryption::bootstrap_cross_signing
98#[derive(Debug, Clone)]
99pub struct UserIdentity {
100 client: Client,
101 inner: CryptoUserIdentity,
102}
103
104impl UserIdentity {
105 pub(crate) fn new(client: Client, identity: CryptoUserIdentity) -> Self {
106 Self { inner: identity, client }
107 }
108
109 #[cfg(feature = "e2e-encryption")]
110 pub(crate) fn underlying_identity(&self) -> CryptoUserIdentity {
111 self.inner.clone()
112 }
113
114 /// The ID of the user this identity belongs to.
115 ///
116 /// # Examples
117 ///
118 /// ```no_run
119 /// # use matrix_sdk::{Client, ruma::user_id};
120 /// # use url::Url;
121 /// # let alice = user_id!("@alice:example.org");
122 /// # let homeserver = Url::parse("http://example.com").unwrap();
123 /// # async {
124 /// # let client = Client::new(homeserver).await.unwrap();
125 /// let user = client.encryption().get_user_identity(alice).await?;
126 ///
127 /// if let Some(user) = user {
128 /// println!("This user identity belongs to {}", user.user_id());
129 /// }
130 ///
131 /// # anyhow::Ok(()) };
132 /// ```
133 pub fn user_id(&self) -> &UserId {
134 match &self.inner {
135 CryptoUserIdentity::Own(identity) => identity.user_id(),
136 CryptoUserIdentity::Other(identity) => identity.user_id(),
137 }
138 }
139
140 /// Request an interactive verification with this `UserIdentity`.
141 ///
142 /// Returns a [`VerificationRequest`] object that can be used to control the
143 /// verification flow.
144 ///
145 /// This will send out a `m.key.verification.request` event. Who such an
146 /// event will be sent to depends on if we're verifying our own identity or
147 /// someone else's:
148 ///
149 /// - Our own identity - All our E2EE capable devices will receive the event
150 /// over to-device messaging.
151 /// - Someone else's identity - The event will be sent to a DM room we share
152 /// with the user, if we don't share a DM with the user, one will be
153 /// created.
154 ///
155 /// The default methods that are supported are:
156 ///
157 /// - `m.sas.v1` - Short auth string, or emoji based verification
158 /// - `m.qr_code.show.v1` - QR code based verification
159 ///
160 /// [`request_verification_with_methods()`] method can be used to override
161 /// this. The `m.qr_code.show.v1` method is only available if the `qrcode`
162 /// feature is enabled, which it is by default.
163 ///
164 /// Check out the [`verification`] module for more info on how to handle
165 /// interactive verifications.
166 ///
167 /// # Examples
168 ///
169 /// ```no_run
170 /// # use matrix_sdk::{Client, ruma::user_id};
171 /// # use url::Url;
172 /// # let alice = user_id!("@alice:example.org");
173 /// # let homeserver = Url::parse("http://example.com").unwrap();
174 /// # async {
175 /// # let client = Client::new(homeserver).await.unwrap();
176 /// let user = client.encryption().get_user_identity(alice).await?;
177 ///
178 /// if let Some(user) = user {
179 /// let verification = user.request_verification().await?;
180 /// }
181 ///
182 /// # anyhow::Ok(()) };
183 /// ```
184 ///
185 /// [`request_verification_with_methods()`]: #method.request_verification_with_methods
186 /// [`verification`]: crate::encryption::verification
187 pub async fn request_verification(
188 &self,
189 ) -> Result<VerificationRequest, RequestVerificationError> {
190 self.request_verification_impl(None).await
191 }
192
193 /// Request an interactive verification with this `UserIdentity` using the
194 /// selected methods.
195 ///
196 /// Returns a [`VerificationRequest`] object that can be used to control the
197 /// verification flow.
198 ///
199 /// This methods behaves the same way as [`request_verification()`], but the
200 /// advertised verification methods can be manually selected.
201 ///
202 /// Check out the [`verification`] module for more info on how to handle
203 /// interactive verifications.
204 ///
205 /// # Arguments
206 ///
207 /// - `methods` - The verification methods that we want to support. Must be
208 /// non-empty.
209 ///
210 /// # Panics
211 ///
212 /// This method will panic if `methods` is empty.
213 ///
214 /// # Examples
215 ///
216 /// ```no_run
217 /// # use matrix_sdk::{
218 /// # Client,
219 /// # ruma::{
220 /// # user_id,
221 /// # events::key::verification::VerificationMethod,
222 /// # }
223 /// # };
224 /// # use url::Url;
225 /// # let alice = user_id!("@alice:example.org");
226 /// # let homeserver = Url::parse("http://example.com").unwrap();
227 /// # async {
228 /// # let client = Client::new(homeserver).await.unwrap();
229 /// let user = client.encryption().get_user_identity(alice).await?;
230 ///
231 /// // We don't want to support showing a QR code, we only support SAS
232 /// // verification
233 /// let methods = vec![VerificationMethod::SasV1];
234 ///
235 /// if let Some(user) = user {
236 /// let verification =
237 /// user.request_verification_with_methods(methods).await?;
238 /// }
239 /// # anyhow::Ok(()) };
240 /// ```
241 ///
242 /// [`request_verification()`]: #method.request_verification
243 /// [`verification`]: crate::encryption::verification
244 pub async fn request_verification_with_methods(
245 &self,
246 methods: Vec<VerificationMethod>,
247 ) -> Result<VerificationRequest, RequestVerificationError> {
248 assert!(!methods.is_empty(), "The list of verification methods can't be non-empty");
249 self.request_verification_impl(Some(methods)).await
250 }
251
252 async fn request_verification_impl(
253 &self,
254 methods: Option<Vec<VerificationMethod>>,
255 ) -> Result<VerificationRequest, RequestVerificationError> {
256 match &self.inner {
257 CryptoUserIdentity::Own(identity) => {
258 let (verification, request) = if let Some(methods) = methods {
259 identity
260 .request_verification_with_methods(methods)
261 .await
262 .map_err(crate::Error::from)?
263 } else {
264 identity.request_verification().await.map_err(crate::Error::from)?
265 };
266
267 self.client.send_verification_request(request).await?;
268
269 Ok(VerificationRequest { inner: verification, client: self.client.clone() })
270 }
271 CryptoUserIdentity::Other(i) => {
272 let content = i.verification_request_content(methods.clone());
273
274 let room = if let Some(room) = self.client.get_dm_room(i.user_id()) {
275 // Make sure that the user, to be verified, is still in the
276 // room
277 if !room
278 .members(RoomMemberships::ACTIVE)
279 .await?
280 .iter()
281 .any(|member| member.user_id() == i.user_id())
282 {
283 room.invite_user_by_id(i.user_id()).await?;
284 }
285 room.clone()
286 } else {
287 self.client.create_dm(i.user_id()).await?
288 };
289
290 let result = room.send(RoomMessageEventContent::new(content)).await?;
291
292 let verification =
293 i.request_verification(room.room_id(), &result.response.event_id, methods);
294
295 Ok(VerificationRequest { inner: verification, client: self.client.clone() })
296 }
297 }
298 }
299
300 /// Manually verify this [`UserIdentity`].
301 ///
302 /// This method will do different things depending on if the user identity
303 /// belongs to us, or if the user identity belongs to someone else. Users
304 /// that chose to manually verify a user identity should make sure that the
305 /// Master key does match to to the `Ed25519` they expect.
306 ///
307 /// The Master key can be inspected using the [`UserIdentity::master_key()`]
308 /// method.
309 ///
310 /// ### Manually verifying other users
311 ///
312 /// This method will attempt to sign the user identity using our private
313 /// parts of the cross signing keys. The method will attempt to sign the
314 /// Master key of the user using our own User-signing key. This will of
315 /// course fail if the private part of the User-signing key isn't available.
316 ///
317 /// The availability of the User-signing key can be checked using the
318 /// [`Encryption::cross_signing_status()`] method.
319 ///
320 /// ### Manually verifying our own user
321 ///
322 /// On the other hand, if the user identity belongs to us, it will be marked
323 /// as verified using a local flag, our own device will also sign the Master
324 /// key. Manually verifying our own user identity can't fail.
325 ///
326 /// ### Problems of manual verification
327 ///
328 /// Manual verification may be more convenient to use, i.e. both users need
329 /// to be online and available to interactively verify each other. Despite
330 /// the convenience, interactive verifications should be generally
331 /// preferred. Manually verifying a user won't notify the other user, the
332 /// one being verified, that they should also verify us. This means that
333 /// user `A` will consider user `B` to be verified, but not the other way
334 /// around.
335 ///
336 /// # Examples
337 ///
338 /// ```no_run
339 /// # use matrix_sdk::{
340 /// # Client,
341 /// # ruma::{
342 /// # user_id,
343 /// # events::key::verification::VerificationMethod,
344 /// # }
345 /// # };
346 /// # use url::Url;
347 /// # let alice = user_id!("@alice:example.org");
348 /// # let homeserver = Url::parse("http://example.com").unwrap();
349 /// # async {
350 /// # let client = Client::new(homeserver).await.unwrap();
351 /// let user = client.encryption().get_user_identity(alice).await?;
352 ///
353 /// if let Some(user) = user {
354 /// user.verify().await?;
355 /// }
356 /// # anyhow::Ok(()) };
357 /// ```
358 ///
359 /// [`Encryption::cross_signing_status()`]: crate::encryption::Encryption::cross_signing_status
360 pub async fn verify(&self) -> Result<(), ManualVerifyError> {
361 let request = match &self.inner {
362 CryptoUserIdentity::Own(identity) => identity.verify().await?,
363 CryptoUserIdentity::Other(identity) => identity.verify().await?,
364 };
365
366 self.client.send(request).await?;
367
368 Ok(())
369 }
370
371 /// Is the user identity considered to be verified.
372 ///
373 /// A user identity is considered to be verified if:
374 ///
375 /// - It has been signed by our User-signing key, if the identity belongs to
376 /// another user
377 /// - If it has been locally marked as verified, if the user identity
378 /// belongs to us.
379 ///
380 /// If the identity belongs to another user, our own user identity needs to
381 /// be verified as well for the identity to be considered to be verified.
382 ///
383 /// # Examples
384 ///
385 /// ```no_run
386 /// # use matrix_sdk::{
387 /// # Client,
388 /// # ruma::{
389 /// # user_id,
390 /// # events::key::verification::VerificationMethod,
391 /// # }
392 /// # };
393 /// # use url::Url;
394 /// # let alice = user_id!("@alice:example.org");
395 /// # let homeserver = Url::parse("http://example.com").unwrap();
396 /// # async {
397 /// # let client = Client::new(homeserver).await.unwrap();
398 /// let user = client.encryption().get_user_identity(alice).await?;
399 ///
400 /// if let Some(user) = user {
401 /// if user.is_verified() {
402 /// println!("User {} is verified", user.user_id());
403 /// } else {
404 /// println!("User {} is not verified", user.user_id());
405 /// }
406 /// }
407 /// # anyhow::Ok(()) };
408 /// ```
409 pub fn is_verified(&self) -> bool {
410 self.inner.is_verified()
411 }
412
413 /// True if we verified this identity at some point in the past.
414 ///
415 /// To reset this latch back to `false`, one must call
416 /// [`UserIdentity::withdraw_verification()`].
417 pub fn was_previously_verified(&self) -> bool {
418 self.inner.was_previously_verified()
419 }
420
421 /// Remove the requirement for this identity to be verified.
422 ///
423 /// If an identity was previously verified and is not anymore it will be
424 /// reported to the user. In order to remove this notice users have to
425 /// verify again or to withdraw the verification requirement.
426 pub async fn withdraw_verification(&self) -> Result<(), CryptoStoreError> {
427 self.inner.withdraw_verification().await
428 }
429
430 /// Was this identity previously verified, and is no longer?
431 pub fn has_verification_violation(&self) -> bool {
432 self.inner.has_verification_violation()
433 }
434
435 /// Remember this identity, ensuring it does not result in a pin violation.
436 ///
437 /// When we first see a user, we assume their cryptographic identity has not
438 /// been tampered with by the homeserver or another entity with
439 /// man-in-the-middle capabilities. We remember this identity and call this
440 /// action "pinning".
441 ///
442 /// If the identity presented for the user changes later on, the newly
443 /// presented identity is considered to be in "pin violation". This method
444 /// explicitly accepts the new identity, allowing it to replace the
445 /// previously pinned one and bringing it out of pin violation.
446 ///
447 /// UIs should display a warning to the user when encountering an identity
448 /// which is not verified and is in pin violation.
449 pub async fn pin(&self) -> Result<(), CryptoStoreError> {
450 self.inner.pin().await
451 }
452
453 /// Get the public part of the Master key of this user identity.
454 ///
455 /// The public part of the Master key is usually used to uniquely identify
456 /// the identity.
457 ///
458 /// # Examples
459 ///
460 /// ```no_run
461 /// # use matrix_sdk::{
462 /// # Client,
463 /// # ruma::{
464 /// # user_id,
465 /// # events::key::verification::VerificationMethod,
466 /// # }
467 /// # };
468 /// # use url::Url;
469 /// # let alice = user_id!("@alice:example.org");
470 /// # let homeserver = Url::parse("http://example.com").unwrap();
471 /// # async {
472 /// # let client = Client::new(homeserver).await.unwrap();
473 /// let user = client.encryption().get_user_identity(alice).await?;
474 ///
475 /// if let Some(user) = user {
476 /// // Let's verify the user after we confirm that the master key
477 /// // matches what we expect, for this we fetch the first public key we
478 /// // can find, there's currently only a single key allowed so this is
479 /// // fine.
480 /// if user.master_key().get_first_key().map(|k| k.to_base64())
481 /// == Some("MyMasterKey".to_string())
482 /// {
483 /// println!(
484 /// "Master keys match for user {}, marking the user as verified",
485 /// user.user_id(),
486 /// );
487 /// user.verify().await?;
488 /// } else {
489 /// println!("Master keys don't match for user {}", user.user_id());
490 /// }
491 /// }
492 /// # anyhow::Ok(()) };
493 /// ```
494 pub fn master_key(&self) -> &MasterPubkey {
495 match &self.inner {
496 CryptoUserIdentity::Own(identity) => identity.master_key(),
497 CryptoUserIdentity::Other(identity) => identity.master_key(),
498 }
499 }
500}