Skip to main content

matrix_sdk_crypto/types/events/
forwarded_room_key.rs

1// Copyright 2022 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
15//! Types for `m.forwarded_room_key` to-device events.
16
17use std::collections::BTreeMap;
18
19use ruma::{DeviceKeyAlgorithm, OwnedRoomId};
20use serde::{Deserialize, Serialize};
21use serde_json::Value;
22use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, megolm::ExportedSessionKey};
23
24use super::{EventType, ToDeviceEvent};
25#[cfg(doc)]
26use crate::olm::InboundGroupSession;
27use crate::types::{
28    EventEncryptionAlgorithm, SigningKeys, deserialize_curve_key, deserialize_curve_key_vec,
29    deserialize_ed25519_key, serialize_curve_key, serialize_curve_key_vec, serialize_ed25519_key,
30};
31
32/// The `m.forwarded_room_key` to-device event.
33pub type ForwardedRoomKeyEvent = ToDeviceEvent<ForwardedRoomKeyContent>;
34
35impl ForwardedRoomKeyEvent {
36    /// Get the algorithm of the forwarded room key.
37    pub fn algorithm(&self) -> EventEncryptionAlgorithm {
38        self.content.algorithm()
39    }
40}
41
42/// The `m.forwarded_room_key` event content.
43///
44/// This is an enum over the different room key algorithms we support. The
45/// currently-supported implementations are used to share
46/// [`InboundGroupSession`]s.
47///
48/// This event type is used to forward keys for end-to-end encryption.
49/// Typically, it is encrypted as an m.room.encrypted event, then sent as a
50/// to-device event.
51///
52/// See <https://spec.matrix.org/v1.13/client-server-api/#mforwarded_room_key>.
53#[derive(Debug, Deserialize)]
54#[serde(try_from = "RoomKeyHelper")]
55pub enum ForwardedRoomKeyContent {
56    /// The `m.megolm.v1.aes-sha2` variant of the `m.forwarded_room_key`
57    /// content.
58    MegolmV1AesSha2(Box<ForwardedMegolmV1AesSha2Content>),
59    /// The `m.megolm.v2.aes-sha2` variant of the `m.forwarded_room_key`
60    /// content.
61    #[cfg(feature = "experimental-algorithms")]
62    MegolmV2AesSha2(Box<ForwardedMegolmV2AesSha2Content>),
63    /// An unknown and unsupported variant of the `m.forwarded_room_key`
64    /// content.
65    Unknown(UnknownRoomKeyContent),
66}
67
68impl ForwardedRoomKeyContent {
69    /// Get the algorithm of the forwarded room key content.
70    pub fn algorithm(&self) -> EventEncryptionAlgorithm {
71        match self {
72            ForwardedRoomKeyContent::MegolmV1AesSha2(_) => {
73                EventEncryptionAlgorithm::MegolmV1AesSha2
74            }
75            #[cfg(feature = "experimental-algorithms")]
76            ForwardedRoomKeyContent::MegolmV2AesSha2(_) => {
77                EventEncryptionAlgorithm::MegolmV2AesSha2
78            }
79            ForwardedRoomKeyContent::Unknown(c) => c.algorithm.to_owned(),
80        }
81    }
82}
83
84impl EventType for ForwardedRoomKeyContent {
85    const EVENT_TYPE: &'static str = "m.forwarded_room_key";
86}
87
88/// The `m.megolm.v1.aes-sha2` variant of the `m.forwarded_room_key` content.
89#[derive(Deserialize, Serialize)]
90pub struct ForwardedMegolmV1AesSha2Content {
91    /// The room where the key is used.
92    pub room_id: OwnedRoomId,
93
94    /// The ID of the session that the key is for.
95    pub session_id: String,
96
97    /// The key to be exchanged. Can be used to create a [`InboundGroupSession`]
98    /// that can be used to decrypt room events.
99    ///
100    /// [`InboundGroupSession`]: vodozemac::megolm::InboundGroupSession
101    pub session_key: ExportedSessionKey,
102
103    /// Chain of Curve25519 keys. It starts out empty, but each time the key is
104    /// forwarded to another device, the previous sender in the chain is added
105    /// to the end of the list.
106    #[serde(
107        deserialize_with = "deserialize_curve_key_vec",
108        serialize_with = "serialize_curve_key_vec"
109    )]
110    pub forwarding_curve25519_key_chain: Vec<Curve25519PublicKey>,
111
112    /// The Curve25519 key of the device which initiated the session originally.
113    ///
114    /// It is ‘claimed’ because the receiving device has no way to tell that
115    /// the original room_key actually came from a device which owns the private
116    /// part of this key.
117    #[serde(
118        rename = "sender_key",
119        deserialize_with = "deserialize_curve_key",
120        serialize_with = "serialize_curve_key"
121    )]
122    pub claimed_sender_key: Curve25519PublicKey,
123
124    /// The Ed25519 key of the device which initiated the session originally.
125    ///
126    /// It is ‘claimed’ because the receiving device has no way to tell that
127    /// the original room_key actually came from a device which owns the private
128    /// part of this key.
129    #[serde(
130        rename = "sender_claimed_ed25519_key",
131        deserialize_with = "deserialize_ed25519_key",
132        serialize_with = "serialize_ed25519_key"
133    )]
134    pub claimed_ed25519_key: Ed25519PublicKey,
135
136    #[serde(flatten)]
137    pub(crate) other: BTreeMap<String, Value>,
138}
139
140/// The `m.megolm.v2.aes-sha2` variant of the `m.forwarded_room_key` content.
141#[derive(Deserialize, Serialize)]
142pub struct ForwardedMegolmV2AesSha2Content {
143    /// The room where the key is used.
144    pub room_id: OwnedRoomId,
145
146    /// The ID of the session that the key is for.
147    pub session_id: String,
148
149    /// The key to be exchanged. Can be used to create a [`InboundGroupSession`]
150    /// that can be used to decrypt room events.
151    ///
152    /// [`InboundGroupSession`]: vodozemac::megolm::InboundGroupSession
153    pub session_key: ExportedSessionKey,
154
155    /// The Curve25519 key of the device which initiated the session originally.
156    ///
157    /// It is ‘claimed’ because the receiving device has no way to tell that
158    /// the original room_key actually came from a device which owns the private
159    /// part of this key.
160    #[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")]
161    pub claimed_sender_key: Curve25519PublicKey,
162
163    /// The Ed25519 key of the device which initiated the session originally.
164    ///
165    /// It is ‘claimed’ because the receiving device has no way to tell that
166    /// the original room_key actually came from a device which owns the private
167    /// part of this key.
168    #[serde(default)]
169    pub claimed_signing_keys: SigningKeys<DeviceKeyAlgorithm>,
170
171    #[serde(flatten)]
172    pub(crate) other: BTreeMap<String, Value>,
173}
174
175/// An unknown and unsupported `m.forwarded_room_key` algorithm.
176#[derive(Clone, Debug, Serialize, Deserialize)]
177pub struct UnknownRoomKeyContent {
178    /// The algorithm of the unknown room key.
179    pub algorithm: EventEncryptionAlgorithm,
180    /// The other data of the unknown room key.
181    #[serde(flatten)]
182    other: BTreeMap<String, Value>,
183}
184
185#[cfg(not(tarpaulin_include))]
186impl std::fmt::Debug for ForwardedMegolmV1AesSha2Content {
187    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188        f.debug_struct("ForwardedMegolmV1AesSha2Content")
189            .field("room_id", &self.room_id)
190            .field("session_id", &self.session_id)
191            .field("forwarding_curve25519_key_chain", &self.forwarding_curve25519_key_chain)
192            .field("claimed_sender_key", &self.claimed_sender_key)
193            .field("claimed_ed25519_key", &self.claimed_ed25519_key)
194            .finish_non_exhaustive()
195    }
196}
197
198#[cfg(not(tarpaulin_include))]
199impl std::fmt::Debug for ForwardedMegolmV2AesSha2Content {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        f.debug_struct("ForwardedMegolmV2AesSha2Content")
202            .field("room_id", &self.room_id)
203            .field("session_id", &self.session_id)
204            .field("claimed_sender_key", &self.claimed_sender_key)
205            .field("sender_claimed_keys", &self.claimed_signing_keys)
206            .finish_non_exhaustive()
207    }
208}
209
210#[derive(Deserialize, Serialize)]
211struct RoomKeyHelper {
212    algorithm: EventEncryptionAlgorithm,
213    #[serde(flatten)]
214    other: Value,
215}
216
217impl TryFrom<RoomKeyHelper> for ForwardedRoomKeyContent {
218    type Error = serde_json::Error;
219
220    fn try_from(value: RoomKeyHelper) -> Result<Self, Self::Error> {
221        Ok(match value.algorithm {
222            EventEncryptionAlgorithm::MegolmV1AesSha2 => {
223                let content: ForwardedMegolmV1AesSha2Content = serde_json::from_value(value.other)?;
224                Self::MegolmV1AesSha2(content.into())
225            }
226            #[cfg(feature = "experimental-algorithms")]
227            EventEncryptionAlgorithm::MegolmV2AesSha2 => {
228                let content: ForwardedMegolmV2AesSha2Content = serde_json::from_value(value.other)?;
229                Self::MegolmV2AesSha2(content.into())
230            }
231            _ => Self::Unknown(UnknownRoomKeyContent {
232                algorithm: value.algorithm,
233                other: serde_json::from_value(value.other)?,
234            }),
235        })
236    }
237}
238
239impl Serialize for ForwardedRoomKeyContent {
240    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
241    where
242        S: serde::Serializer,
243    {
244        let helper = match self {
245            Self::MegolmV1AesSha2(r) => RoomKeyHelper {
246                algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
247                other: serde_json::to_value(r).map_err(serde::ser::Error::custom)?,
248            },
249            #[cfg(feature = "experimental-algorithms")]
250            Self::MegolmV2AesSha2(r) => RoomKeyHelper {
251                algorithm: EventEncryptionAlgorithm::MegolmV2AesSha2,
252                other: serde_json::to_value(r).map_err(serde::ser::Error::custom)?,
253            },
254            Self::Unknown(r) => RoomKeyHelper {
255                algorithm: r.algorithm.clone(),
256                other: serde_json::to_value(r.other.clone()).map_err(serde::ser::Error::custom)?,
257            },
258        };
259
260        helper.serialize(serializer)
261    }
262}