core_crypto/mls/conversation/conversation_guard/
encrypt.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! The methods in this module are concerned with message encryption.

use super::ConversationGuard;
use super::Result;
use crate::MlsError;
use openmls::prelude::MlsMessageOutBody;

impl ConversationGuard {
    /// Encrypts a raw payload then serializes it to the TLS wire format
    /// Can only be called when there is no pending commit and no pending proposal.
    ///
    /// # Arguments
    /// * `message` - the message as a byte array
    ///
    /// # Return type
    /// This method will return an encrypted TLS serialized message.
    ///
    /// # Errors
    /// If the conversation can't be found, an error will be returned. Other errors are originating
    /// from OpenMls and the KeyStore
    pub async fn encrypt_message(&mut self, message: impl AsRef<[u8]>) -> Result<Vec<u8>> {
        let backend = self.mls_provider().await?;
        let credential = self.credential_bundle().await?;
        let signer = credential.signature_key();
        let mut inner = self.conversation_mut().await;
        let encrypted = inner
            .group
            .create_message(&backend, signer, message.as_ref())
            .map_err(MlsError::wrap("creating message"))?;

        // make sure all application messages are encrypted
        debug_assert!(matches!(encrypted.body, MlsMessageOutBody::PrivateMessage(_)));

        let encrypted = encrypted
            .to_bytes()
            .map_err(MlsError::wrap("constructing byte vector of encrypted message"))?;

        inner.persist_group_when_changed(&backend.keystore(), false).await?;
        Ok(encrypted)
    }
}

#[cfg(test)]
mod tests {
    use wasm_bindgen_test::*;

    use crate::test_utils::*;

    wasm_bindgen_test_configure!(run_in_browser);

    #[apply(all_cred_cipher)]
    #[wasm_bindgen_test]
    async fn can_encrypt_app_message(case: TestCase) {
        run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| {
            Box::pin(async move {
                let id = conversation_id();
                alice_central
                    .context
                    .new_conversation(&id, case.credential_type, case.cfg.clone())
                    .await
                    .unwrap();
                alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap();

                let msg = b"Hello bob";
                let encrypted = alice_central
                    .context
                    .conversation_guard(&id)
                    .await
                    .unwrap()
                    .encrypt_message(msg)
                    .await
                    .unwrap();
                assert_ne!(&msg[..], &encrypted[..]);
                let decrypted = bob_central
                    .context
                    .decrypt_message(&id, encrypted)
                    .await
                    .unwrap()
                    .app_msg
                    .unwrap();
                assert_eq!(&decrypted[..], &msg[..]);
            })
        })
        .await
    }

    // Ensures encrypting an application message is durable
    #[apply(all_cred_cipher)]
    #[wasm_bindgen_test]
    async fn can_encrypt_consecutive_messages(case: TestCase) {
        run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| {
            Box::pin(async move {
                let id = conversation_id();
                alice_central
                    .context
                    .new_conversation(&id, case.credential_type, case.cfg.clone())
                    .await
                    .unwrap();
                alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap();
                let mut alice_conversation = alice_central.context.conversation_guard(&id).await.unwrap();

                let msg = b"Hello bob";
                let encrypted = alice_conversation.encrypt_message(msg).await.unwrap();
                assert_ne!(&msg[..], &encrypted[..]);
                let decrypted = bob_central
                    .context
                    .decrypt_message(&id, encrypted)
                    .await
                    .unwrap()
                    .app_msg
                    .unwrap();
                assert_eq!(&decrypted[..], &msg[..]);

                let msg = b"Hello bob again";
                let encrypted = alice_conversation.encrypt_message(msg).await.unwrap();
                assert_ne!(&msg[..], &encrypted[..]);
                let decrypted = bob_central
                    .context
                    .decrypt_message(&id, encrypted)
                    .await
                    .unwrap()
                    .app_msg
                    .unwrap();
                assert_eq!(&decrypted[..], &msg[..]);
            })
        })
        .await
    }
}