Skip to main content

core_crypto/mls/conversation/immutable/
mod.rs

1mod clients;
2mod commit_delay;
3mod credential;
4mod duplicate;
5mod e2ei;
6mod history_sharing;
7mod persistence;
8
9use async_lock::{RwLock, RwLockReadGuard};
10use openmls::group::MlsGroup;
11
12use super::{ConversationIdRef, Error, ExternalSenderKey, Result, SecretKey};
13use crate::{CipherSuite, ConversationConfiguration, ConversationId, CredentialRef, OpenMlsError, Session};
14
15/// A Conversation exposes the read-only interface of an MLS conversation.
16#[derive(Debug, derive_more::Constructor)]
17pub struct Conversation {
18    pub(in crate::mls::conversation) id: ConversationId,
19    pub(in crate::mls::conversation) group: RwLock<MlsGroup>,
20    pub(in crate::mls::conversation) configuration: ConversationConfiguration,
21    session: Session,
22}
23
24impl Conversation {
25    /// Returns the conversation's ID
26    pub fn id(&self) -> &ConversationIdRef {
27        ConversationIdRef::new(&self.id)
28    }
29
30    /// Returns an immutable guard over the underlying MLS group
31    pub async fn group(&self) -> RwLockReadGuard<'_, MlsGroup> {
32        self.group.read().await
33    }
34
35    /// Returns the conversation's configuration
36    pub fn configuration(&self) -> &ConversationConfiguration {
37        &self.configuration
38    }
39
40    /// Returns current epoch of the MLS group
41    pub async fn epoch(&self) -> u64 {
42        self.group().await.epoch().as_u64()
43    }
44
45    /// Returns this conversation's cipher suite
46    pub fn cipher_suite(&self) -> CipherSuite {
47        self.configuration.cipher_suite
48    }
49
50    /// Returns a reference to the credential used in this conversation
51    pub async fn credential_ref(&self) -> Result<CredentialRef> {
52        let credential = self
53            .find_current_credential()
54            .await
55            .map_err(|_| Error::IdentityInitializationError)?;
56        Ok(CredentialRef::from_credential(&credential))
57    }
58
59    /// Derives a new key from the one in the group, to be used elsewhere.
60    ///
61    /// # Arguments
62    /// * `key_length` - the length of the key to be derived. If the value is higher than the bounds of `u16` or the
63    ///   context hash * 255, an error will be returned
64    ///
65    /// # Errors
66    /// OpenMls secret generation error
67    pub async fn export_secret_key(&self, key_length: usize) -> Result<SecretKey> {
68        const EXPORTER_LABEL: &str = "exporter";
69        const EXPORTER_CONTEXT: &[u8] = &[];
70        self.group()
71            .await
72            .export_secret(
73                &self.session.crypto_provider,
74                EXPORTER_LABEL,
75                EXPORTER_CONTEXT,
76                key_length,
77            )
78            .map(Into::into)
79            .map_err(OpenMlsError::wrap("exporting secret key"))
80            .map_err(Into::into)
81    }
82
83    /// Returns the raw public key of the first external sender present in this group.
84    ///
85    /// This should be used to initialize a subconversation
86    pub async fn get_external_sender(&self) -> Result<ExternalSenderKey> {
87        let group = self.group().await;
88        let ext_senders = group
89            .group_context_extensions()
90            .external_senders()
91            .ok_or(Error::MissingExternalSenderExtension)?;
92        let ext_sender = ext_senders.first().ok_or(Error::MissingExternalSenderExtension)?;
93        let ext_sender_public_key = ext_sender.signature_key().as_slice().to_vec().into();
94        Ok(ext_sender_public_key)
95    }
96}
97
98#[cfg(test)]
99mod test_utils {
100    use openmls::prelude::SignaturePublicKey;
101
102    use super::*;
103
104    impl Conversation {
105        pub async fn signature_keys(&self) -> Vec<SignaturePublicKey> {
106            let group = self.group().await;
107            group
108                .members()
109                .map(|m| m.signature_key)
110                .map(|mpk| SignaturePublicKey::from(mpk.as_slice()))
111                .collect()
112        }
113
114        pub async fn encryption_keys(&self) -> Vec<Vec<u8>> {
115            let group = self.group().await;
116            group.members().map(|m| m.encryption_key).collect()
117        }
118
119        pub async fn extensions(&self) -> openmls::prelude::Extensions {
120            let group = self.group().await;
121            group.export_group_context().extensions().to_owned()
122        }
123    }
124}