Skip to main content

core_crypto/mls/conversation/immutable/
credential.rs

1use std::{collections::HashMap, sync::Arc};
2
3use openmls::{
4    group::QueuedProposal,
5    prelude::{
6        Credential as MlsCredential, CredentialWithKey, LeafNode, LeafNodeIndex, Proposal, Sender, SignaturePublicKey,
7    },
8};
9
10use super::{Error, Result};
11use crate::{Credential, LeafError, RecursiveError};
12
13impl super::Conversation {
14    fn extract_own_updated_node_from_proposals<'a>(
15        own_index: &LeafNodeIndex,
16        pending_proposals: impl Iterator<Item = &'a QueuedProposal>,
17    ) -> Option<&'a LeafNode> {
18        pending_proposals
19            .filter_map(|proposal| {
20                if let Sender::Member(index) = proposal.sender()
21                    && index == own_index
22                    && let Proposal::Update(update_proposal) = proposal.proposal()
23                {
24                    Some(update_proposal.leaf_node())
25                } else {
26                    None
27                }
28            })
29            .last()
30    }
31
32    /// Find the current leaf node, then load it scredential.
33    pub(crate) async fn find_current_credential(&self) -> Result<Arc<Credential>> {
34        // if the group has pending proposals one of which is an own update proposal, we should take the credential from
35        // there.
36        let group = self.group().await;
37        let own_leaf =
38            Self::extract_own_updated_node_from_proposals(&group.own_leaf_index(), group.pending_proposals())
39                .or_else(|| group.own_leaf())
40                .ok_or(LeafError::InternalMlsError)?;
41        let credential = self
42            .session
43            .find_credential_by_public_key(own_leaf.signature_key())
44            .await
45            .map_err(RecursiveError::mls_client("finding current credential"))?;
46        Ok(credential)
47    }
48
49    /// Returns all members credentials from the group/conversation
50    pub async fn members(&self) -> HashMap<Vec<u8>, MlsCredential> {
51        // this fold is the compact way to express this:
52        // a normal `.map().collect()` would preserve later instances of duplicated keys,
53        // but this preserves the first instance of each key
54        self.group().await.members().fold(HashMap::new(), |mut acc, kp| {
55            let credential = kp.credential;
56            let id = credential.identity().to_vec();
57            acc.entry(id).or_insert(credential);
58            acc
59        })
60    }
61
62    /// Returns all members credentials with their signature public key from the group/conversation
63    pub async fn members_with_key(&self) -> HashMap<Vec<u8>, CredentialWithKey> {
64        self.group().await.members().fold(HashMap::new(), |mut acc, kp| {
65            let credential = kp.credential;
66            let id = credential.identity().to_vec();
67            let signature_key = SignaturePublicKey::from(kp.signature_key);
68            let credential = CredentialWithKey {
69                credential,
70                signature_key,
71            };
72            acc.entry(id).or_insert(credential);
73            acc
74        })
75    }
76
77    pub(crate) async fn own_mls_credential(&self) -> Result<MlsCredential> {
78        let credential = self
79            .group()
80            .await
81            .own_leaf_node()
82            .ok_or(Error::MlsGroupInvalidState("own_leaf_node not present in group"))?
83            .credential()
84            .to_owned();
85        Ok(credential)
86    }
87}