Skip to main content

core_crypto/mls/conversation/immutable/
e2ei.rs

1use std::{borrow::Borrow, collections::HashMap};
2
3use openmls_traits::OpenMlsCryptoProvider as _;
4use uuid::Uuid;
5
6use super::{Error, Result};
7use crate::{
8    ClientIdRef, CredentialType, E2eiConversationState, RecursiveError, Session, WireIdentity,
9    mls::credential::ext::CredentialExt as _,
10};
11
12impl super::Conversation {
13    /// Indicates when to mark a conversation as not verified i.e. when not all its members have a X509
14    /// Credential generated by Wire's end-to-end identity enrollment
15    pub async fn e2ei_conversation_state(&self) -> Result<E2eiConversationState> {
16        let backend = &self.session.crypto_provider;
17
18        let state = Session::compute_conversation_state(
19            self.cipher_suite(),
20            self.group().await.members_credentials(),
21            CredentialType::X509,
22            backend.authentication_service(),
23        )
24        .await;
25        Ok(state)
26    }
27
28    /// From a given conversation, get the identity of the members supplied. Identity is only present for
29    /// members with a Certificate Credential (after turning on end-to-end identity).
30    /// If no member has a x509 certificate, it will return an empty Vec
31    pub async fn get_device_identities(
32        &self,
33        device_ids: &[impl Borrow<ClientIdRef> + Sync],
34    ) -> Result<Vec<WireIdentity>> {
35        if device_ids.is_empty() {
36            return Err(Error::CallerError(
37                "This function accepts a list of IDs as a parameter, but that list was empty.",
38            ));
39        }
40        let mls_provider = &self.session.crypto_provider;
41        let auth_service = mls_provider.authentication_service();
42
43        let pki_env = auth_service.pki_env().await;
44
45        let mut identities = vec![];
46        for (id, credential) in self.members_with_key().await? {
47            if device_ids.iter().any(|client_id| client_id.borrow() == id.as_slice()) {
48                identities.push(
49                    credential
50                        .extract_identity(self.cipher_suite(), pki_env.as_deref())
51                        .await
52                        .map_err(RecursiveError::mls_credential("extracting identity"))?,
53                );
54            }
55        }
56        Ok(identities)
57    }
58
59    /// From a given conversation, get the identity of the users (device holders) supplied.
60    /// Identity is only present for devices with a Certificate Credential (after turning on end-to-end identity).
61    /// If no member has a x509 certificate, it will return an empty Vec.
62    ///
63    /// Returns a Map with all the identities for a given users. Consumers are then recommended to
64    /// reduce those identities to determine the actual status of a user.
65    pub async fn get_user_identities(&self, user_ids: &[Uuid]) -> Result<HashMap<Uuid, Vec<WireIdentity>>> {
66        if user_ids.is_empty() {
67            return Err(Error::CallerError(
68                "This function accepts a list of IDs as a parameter, but that list was empty.",
69            ));
70        }
71        let mls_provider = &self.session.crypto_provider;
72        let auth_service = mls_provider.authentication_service();
73
74        let pki_env = auth_service.pki_env().await;
75
76        let mut identities = HashMap::new();
77        for (id, credential) in self.members_with_key().await? {
78            let user_id = &id.deserialize().user_id;
79
80            if !user_ids.contains(user_id) {
81                continue;
82            }
83
84            let identity = credential
85                .extract_identity(self.cipher_suite(), pki_env.as_deref())
86                .await
87                .map_err(RecursiveError::mls_credential("extracting identity"))?;
88            let value = identities.entry(*user_id).or_insert_with(Vec::new);
89            value.push(identity);
90        }
91
92        Ok(identities)
93    }
94}