core_crypto/mls/conversation/conversation_guard/
encrypt.rs

1//! The methods in this module are concerned with message encryption.
2
3use super::ConversationGuard;
4use super::Result;
5use crate::MlsError;
6use crate::mls::conversation::ConversationWithMls as _;
7use openmls::prelude::MlsMessageOutBody;
8
9impl ConversationGuard {
10    /// Encrypts a raw payload then serializes it to the TLS wire format
11    /// Can only be called when there is no pending commit and no pending proposal.
12    ///
13    /// # Arguments
14    /// * `message` - the message as a byte array
15    ///
16    /// # Return type
17    /// This method will return an encrypted TLS serialized message.
18    ///
19    /// # Errors
20    /// If the conversation can't be found, an error will be returned. Other errors are originating
21    /// from OpenMls and the KeyStore
22    pub async fn encrypt_message(&mut self, message: impl AsRef<[u8]>) -> Result<Vec<u8>> {
23        let backend = self.mls_provider().await?;
24        let credential = self.credential_bundle().await?;
25        let signer = credential.signature_key();
26        let mut inner = self.conversation_mut().await;
27        let encrypted = inner
28            .group
29            .create_message(&backend, signer, message.as_ref())
30            .map_err(MlsError::wrap("creating message"))?;
31
32        // make sure all application messages are encrypted
33        debug_assert!(matches!(encrypted.body, MlsMessageOutBody::PrivateMessage(_)));
34
35        let encrypted = encrypted
36            .to_bytes()
37            .map_err(MlsError::wrap("constructing byte vector of encrypted message"))?;
38
39        inner.persist_group_when_changed(&backend.keystore(), false).await?;
40        Ok(encrypted)
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use wasm_bindgen_test::*;
47
48    use crate::test_utils::*;
49
50    wasm_bindgen_test_configure!(run_in_browser);
51
52    #[apply(all_cred_cipher)]
53    #[wasm_bindgen_test]
54    async fn can_encrypt_app_message(case: TestCase) {
55        run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| {
56            Box::pin(async move {
57                let id = conversation_id();
58                alice_central
59                    .context
60                    .new_conversation(&id, case.credential_type, case.cfg.clone())
61                    .await
62                    .unwrap();
63                alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap();
64
65                let msg = b"Hello bob";
66                let encrypted = alice_central
67                    .context
68                    .conversation(&id)
69                    .await
70                    .unwrap()
71                    .encrypt_message(msg)
72                    .await
73                    .unwrap();
74                assert_ne!(&msg[..], &encrypted[..]);
75                let decrypted = bob_central
76                    .context
77                    .conversation(&id)
78                    .await
79                    .unwrap()
80                    .decrypt_message(encrypted)
81                    .await
82                    .unwrap()
83                    .app_msg
84                    .unwrap();
85                assert_eq!(&decrypted[..], &msg[..]);
86            })
87        })
88        .await
89    }
90
91    // Ensures encrypting an application message is durable
92    #[apply(all_cred_cipher)]
93    #[wasm_bindgen_test]
94    async fn can_encrypt_consecutive_messages(case: TestCase) {
95        run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| {
96            Box::pin(async move {
97                let id = conversation_id();
98                alice_central
99                    .context
100                    .new_conversation(&id, case.credential_type, case.cfg.clone())
101                    .await
102                    .unwrap();
103                alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap();
104                let mut alice_conversation = alice_central.context.conversation(&id).await.unwrap();
105
106                let msg = b"Hello bob";
107                let encrypted = alice_conversation.encrypt_message(msg).await.unwrap();
108                assert_ne!(&msg[..], &encrypted[..]);
109                let decrypted = bob_central
110                    .context
111                    .conversation(&id)
112                    .await
113                    .unwrap()
114                    .decrypt_message(encrypted)
115                    .await
116                    .unwrap()
117                    .app_msg
118                    .unwrap();
119                assert_eq!(&decrypted[..], &msg[..]);
120
121                let msg = b"Hello bob again";
122                let encrypted = alice_conversation.encrypt_message(msg).await.unwrap();
123                assert_ne!(&msg[..], &encrypted[..]);
124                let decrypted = bob_central
125                    .context
126                    .conversation(&id)
127                    .await
128                    .unwrap()
129                    .decrypt_message(encrypted)
130                    .await
131                    .unwrap()
132                    .app_msg
133                    .unwrap();
134                assert_eq!(&decrypted[..], &msg[..]);
135            })
136        })
137        .await
138    }
139}