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.crypto_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: TestContext) {
55        let [alice_central, bob_central] = case.sessions().await;
56        Box::pin(async move {
57            let id = conversation_id();
58            alice_central
59                .transaction
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                .transaction
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                .transaction
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        .await
88    }
89
90    // Ensures encrypting an application message is durable
91    #[apply(all_cred_cipher)]
92    #[wasm_bindgen_test]
93    async fn can_encrypt_consecutive_messages(case: TestContext) {
94        let [alice_central, bob_central] = case.sessions().await;
95        Box::pin(async move {
96            let id = conversation_id();
97            alice_central
98                .transaction
99                .new_conversation(&id, case.credential_type, case.cfg.clone())
100                .await
101                .unwrap();
102            alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap();
103            let mut alice_conversation = alice_central.transaction.conversation(&id).await.unwrap();
104
105            let msg = b"Hello bob";
106            let encrypted = alice_conversation.encrypt_message(msg).await.unwrap();
107            assert_ne!(&msg[..], &encrypted[..]);
108            let decrypted = bob_central
109                .transaction
110                .conversation(&id)
111                .await
112                .unwrap()
113                .decrypt_message(encrypted)
114                .await
115                .unwrap()
116                .app_msg
117                .unwrap();
118            assert_eq!(&decrypted[..], &msg[..]);
119
120            let msg = b"Hello bob again";
121            let encrypted = alice_conversation.encrypt_message(msg).await.unwrap();
122            assert_ne!(&msg[..], &encrypted[..]);
123            let decrypted = bob_central
124                .transaction
125                .conversation(&id)
126                .await
127                .unwrap()
128                .decrypt_message(encrypted)
129                .await
130                .unwrap()
131                .app_msg
132                .unwrap();
133            assert_eq!(&decrypted[..], &msg[..]);
134        })
135        .await
136    }
137}