matrix_sdk_base/response_processors/e2ee/
to_device.rs

1// Copyright 2025 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_crypto::{store::RoomKeyInfo, EncryptionSyncChanges, OlmMachine};
18use ruma::{
19    api::client::sync::sync_events::{v3, v5, DeviceLists},
20    events::AnyToDeviceEvent,
21    serde::Raw,
22    OneTimeKeyAlgorithm, UInt,
23};
24
25use super::super::Context;
26use crate::Result;
27
28/// Process the to-device events and other related e2ee data based on a response
29/// from a [MSC4186 request][`v5`].
30///
31/// This returns a list of all the to-device events that were passed in but
32/// encrypted ones were replaced with their decrypted version.
33pub async fn from_msc4186(
34    context: &mut Context,
35    to_device: Option<&v5::response::ToDevice>,
36    e2ee: &v5::response::E2EE,
37    olm_machine: Option<&OlmMachine>,
38) -> Result<Output> {
39    process(
40        context,
41        olm_machine,
42        to_device.as_ref().map(|to_device| to_device.events.clone()).unwrap_or_default(),
43        &e2ee.device_lists,
44        &e2ee.device_one_time_keys_count,
45        e2ee.device_unused_fallback_key_types.as_deref(),
46        to_device.as_ref().map(|to_device| to_device.next_batch.clone()),
47    )
48    .await
49}
50
51/// Process the to-device events and other related e2ee data based on a response
52/// from a [`/v3/sync` request][`v3`].
53///
54/// This returns a list of all the to-device events that were passed in but
55/// encrypted ones were replaced with their decrypted version.
56pub async fn from_sync_v2(
57    context: &mut Context,
58    response: &v3::Response,
59    olm_machine: Option<&OlmMachine>,
60) -> Result<Output> {
61    process(
62        context,
63        olm_machine,
64        response.to_device.events.clone(),
65        &response.device_lists,
66        &response.device_one_time_keys_count,
67        response.device_unused_fallback_key_types.as_deref(),
68        Some(response.next_batch.clone()),
69    )
70    .await
71}
72
73/// Process the to-device events and other related e2ee data.
74///
75/// This returns a list of all the to-device events that were passed in but
76/// encrypted ones were replaced with their decrypted version.
77async fn process(
78    _context: &mut Context,
79    olm_machine: Option<&OlmMachine>,
80    to_device_events: Vec<Raw<AnyToDeviceEvent>>,
81    device_lists: &DeviceLists,
82    one_time_keys_counts: &BTreeMap<OneTimeKeyAlgorithm, UInt>,
83    unused_fallback_keys: Option<&[OneTimeKeyAlgorithm]>,
84    next_batch_token: Option<String>,
85) -> Result<Output> {
86    let encryption_sync_changes = EncryptionSyncChanges {
87        to_device_events,
88        changed_devices: device_lists,
89        one_time_keys_counts,
90        unused_fallback_keys,
91        next_batch_token,
92    };
93
94    Ok(if let Some(olm_machine) = olm_machine {
95        // Let the crypto machine handle the sync response, this
96        // decrypts to-device events, but leaves room events alone.
97        // This makes sure that we have the decryption keys for the room
98        // events at hand.
99        let (events, room_key_updates) =
100            olm_machine.receive_sync_changes(encryption_sync_changes).await?;
101
102        let events = events
103            .iter()
104            // TODO: There is loss of information here, after calling `to_raw` it is not
105            // possible to make the difference between a successfully decrypted event and a plain
106            // text event. This information needs to be propagated to top layer at some point if
107            // clients relies on custom encrypted to device events.
108            .map(|p| p.to_raw())
109            .collect();
110
111        Output { decrypted_to_device_events: events, room_key_updates: Some(room_key_updates) }
112    } else {
113        // If we have no `OlmMachine`, just return the events that were passed in.
114        // This should not happen unless we forget to set things up by calling
115        // `Self::activate()`.
116        Output {
117            decrypted_to_device_events: encryption_sync_changes.to_device_events,
118            room_key_updates: None,
119        }
120    })
121}
122
123pub struct Output {
124    pub decrypted_to_device_events: Vec<Raw<AnyToDeviceEvent>>,
125    pub room_key_updates: Option<Vec<RoomKeyInfo>>,
126}