Skip to main content

ruma_events/
room_key_bundle.rs

1//! Types for the `m.room_key_bundle` event defined in [MSC4268].
2//!
3//! [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
4
5use ruma_common::OwnedRoomId;
6use ruma_macros::EventContent;
7use serde::{Deserialize, Serialize};
8
9use crate::room::EncryptedFile;
10
11/// The content of an `m.room_key_bundle` event.
12///
13/// Typically encrypted as an `m.room.encrypted` event, then sent as a to-device event.
14///
15/// This event is defined in [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268)
16#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
17#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
18#[ruma_event(type = "io.element.msc4268.room_key_bundle", alias = "m.room_key_bundle", kind = ToDevice)]
19pub struct ToDeviceRoomKeyBundleEventContent {
20    /// The room that these keys are for.
21    pub room_id: OwnedRoomId,
22
23    /// The location and encryption info of the key bundle.
24    pub file: EncryptedFile,
25}
26
27impl ToDeviceRoomKeyBundleEventContent {
28    /// Creates a new `ToDeviceRoomKeyBundleEventContent` with the given room ID, and
29    /// [`EncryptedFile`] which contains the room keys from the bundle.
30    pub fn new(room_id: OwnedRoomId, file: EncryptedFile) -> Self {
31        Self { room_id, file }
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use ruma_common::{
38        canonical_json::assert_to_canonical_json_eq, owned_mxc_uri, owned_room_id, serde::Base64,
39    };
40    use serde_json::json;
41
42    use super::ToDeviceRoomKeyBundleEventContent;
43    use crate::room::{EncryptedFile, EncryptedFileHash, V2EncryptedFileInfo};
44
45    #[test]
46    fn serialization() {
47        let content = ToDeviceRoomKeyBundleEventContent {
48            room_id: owned_room_id!("!testroomid:example.org"),
49            file: EncryptedFile::new(
50                owned_mxc_uri!("mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe"),
51                V2EncryptedFileInfo::new(
52                    Base64::parse("aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0").unwrap(),
53                    Base64::parse("w+sE15fzSc0AAAAAAAAAAA").unwrap(),
54                )
55                .into(),
56                std::iter::once(EncryptedFileHash::Sha256(
57                    Base64::parse("fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA").unwrap(),
58                ))
59                .collect(),
60            ),
61        };
62
63        assert_to_canonical_json_eq!(
64            content,
65            json!({
66                "room_id": "!testroomid:example.org",
67                "file": {
68                    "v": "v2",
69                    "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
70                    "key": {
71                        "alg": "A256CTR",
72                        "ext": true,
73                        "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0",
74                        "key_ops": ["decrypt", "encrypt"],
75                        "kty": "oct"
76                    },
77                    "iv": "w+sE15fzSc0AAAAAAAAAAA",
78                    "hashes": {
79                        "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA"
80                    }
81                }
82            }),
83        );
84    }
85}