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