Skip to main content

matrix_sdk_crypto/types/events/
secret_push.rs

1// Copyright 2026 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 `io.element.msc4385.secret.push` to-device events.
16
17// This is here because we have a zeroize(skip) further below, which incorrectly triggers a
18// unused_assignments warning due to the macro not using a variable.
19//
20// This will be fixed once we bump Zeroize.
21#![allow(unused_assignments)]
22
23use std::collections::BTreeMap;
24
25use ruma::events::secret::request::SecretName;
26use serde::{Deserialize, Serialize};
27use serde_json::Value;
28use zeroize::{Zeroize, ZeroizeOnDrop};
29
30use super::{EventType, ToDeviceEvent};
31
32/// The `io.element.msc4385.secret.push` to-device event.
33pub type SecretPushEvent = ToDeviceEvent<SecretPushContent>;
34
35/// The `io.element.msc4385.secret.push` event content.
36///
37/// Sent by a client to push a secret with another device. It must be encrypted
38/// as an `m.room.encrypted` event, then sent as a to-device event.
39#[derive(Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
40pub struct SecretPushContent {
41    /// The name of the secret.
42    #[zeroize(skip)]
43    pub name: SecretName,
44    /// The contents of the secret.
45    pub secret: String,
46    /// Any other, custom and non-specced fields of the content.
47    #[serde(flatten)]
48    #[zeroize(skip)]
49    other: BTreeMap<String, Value>,
50}
51
52impl SecretPushContent {
53    /// Create a new `io.element.msc4385.secret.push` content.
54    pub fn new(name: SecretName, secret: String) -> Self {
55        Self { name, secret, other: Default::default() }
56    }
57}
58
59#[cfg(not(tarpaulin_include))]
60impl std::fmt::Debug for SecretPushContent {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        f.debug_struct("SecretPushContent").field("name", &self.name).finish_non_exhaustive()
63    }
64}
65
66impl EventType for SecretPushContent {
67    const EVENT_TYPE: &'static str = "io.element.msc4385.secret.push";
68}
69
70#[cfg(test)]
71pub(crate) mod tests {
72    use serde_json::{Value, json};
73
74    use super::SecretPushEvent;
75
76    pub(crate) fn json() -> Value {
77        json!({
78            "sender": "@alice:example.org",
79            "content": {
80                "name": "org.example.secret.name",
81                "secret": "ThisIsASecretDon'tTellAnyone"
82            },
83            "type": "io.element.msc4385.secret.push",
84        })
85    }
86
87    #[test]
88    fn deserialization() -> Result<(), serde_json::Error> {
89        let json = json();
90        let event: SecretPushEvent = serde_json::from_value(json.clone())?;
91
92        assert_eq!(event.content.name.as_str(), "org.example.secret.name");
93        assert_eq!(&event.content.secret, "ThisIsASecretDon'tTellAnyone");
94
95        let serialized = serde_json::to_value(event)?;
96        assert_eq!(json, serialized);
97
98        Ok(())
99    }
100}