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    ephemeral::is_history_client, 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.group().await.members_credentials(),
20            CredentialType::X509,
21            backend.authentication_service(),
22        )
23        .await;
24        Ok(state)
25    }
26
27    /// From a given conversation, get the identity of the members supplied.
28    ///
29    /// An identity is returned for every requested member which is in the
30    /// conversation, whatever its credential type. Members with an X509
31    /// credential additionally carry a [`WireIdentity::x509_identity`]; members
32    /// with a basic credential do not, and their `status` is always `Valid`,
33    /// which on its own is no evidence of end-to-end identity enrolment.
34    pub async fn get_device_identities(
35        &self,
36        device_ids: &[impl Borrow<ClientIdRef> + Sync],
37    ) -> Result<Vec<WireIdentity>> {
38        if device_ids.is_empty() {
39            return Err(Error::CallerError(
40                "This function accepts a list of IDs as a parameter, but that list was empty.",
41            ));
42        }
43        let mls_provider = &self.session.crypto_provider;
44        let auth_service = mls_provider.authentication_service();
45
46        let pki_env = auth_service.pki_env().await;
47
48        let mut identities = vec![];
49        for (id, credential) in self.members_with_key().await? {
50            if device_ids.iter().any(|client_id| client_id.borrow() == id.as_slice()) {
51                identities.push(
52                    credential
53                        .extract_identity(self.cipher_suite().signature_algorithm(), pki_env.as_deref())
54                        .await
55                        .map_err(RecursiveError::context("extracting identity"))?,
56                );
57            }
58        }
59        Ok(identities)
60    }
61
62    /// From a given conversation, get the identity of the users (device holders) supplied.
63    ///
64    /// As with [`Self::get_device_identities`], an identity is returned for every device of every
65    /// requested user, whatever its credential type; only devices with an X509 credential carry a
66    /// [`WireIdentity::x509_identity`].
67    ///
68    /// Returns a Map with all the identities for a given users. Consumers are then recommended to
69    /// reduce those identities to determine the actual status of a user.
70    pub async fn get_user_identities(&self, user_ids: &[Uuid]) -> Result<HashMap<Uuid, Vec<WireIdentity>>> {
71        if user_ids.is_empty() {
72            return Err(Error::CallerError(
73                "This function accepts a list of IDs as a parameter, but that list was empty.",
74            ));
75        }
76        let mls_provider = &self.session.crypto_provider;
77        let auth_service = mls_provider.authentication_service();
78
79        let pki_env = auth_service.pki_env().await;
80
81        let mut identities = HashMap::new();
82        for (id, credential) in self.members_with_key().await? {
83            // history clients don't belong to any user
84            if is_history_client(&id) {
85                continue;
86            }
87
88            let user_id = &id
89                .deserialize()
90                .map_err(RecursiveError::mls_client("deserializing client id"))?
91                .user_id;
92
93            if !user_ids.contains(user_id) {
94                continue;
95            }
96
97            let identity = credential
98                .extract_identity(self.cipher_suite().signature_algorithm(), pki_env.as_deref())
99                .await
100                .map_err(RecursiveError::context("extracting identity"))?;
101            let value = identities.entry(*user_id).or_insert_with(Vec::new);
102            value.push(identity);
103        }
104
105        Ok(identities)
106    }
107}