core_crypto/mls/session/
e2e_identity.rs1use openmls::{
2 prelude::{Credential, Node, SignatureScheme, group_info::VerifiableGroupInfo},
3 treesync::RatchetTree,
4};
5use openmls_traits::OpenMlsCryptoProvider as _;
6use wire_e2e_identity::prelude::WireIdentityReader as _;
7
8use super::{Error, Result, Session};
9use crate::{E2eiConversationState, MlsCiphersuite, MlsCredentialType, MlsError, mls::session::CredentialExt as _};
10
11impl Session {
12 pub async fn e2ei_is_pki_env_setup(&self) -> bool {
14 self.crypto_provider.is_pki_env_setup().await
15 }
16
17 pub async fn e2ei_is_enabled(&self, signature_scheme: SignatureScheme) -> Result<bool> {
19 let x509_result = self
20 .find_most_recent_credential_bundle(signature_scheme, MlsCredentialType::X509)
21 .await;
22 match x509_result {
23 Err(Error::CredentialNotFound(MlsCredentialType::X509)) => {
24 self.find_most_recent_credential_bundle(signature_scheme, MlsCredentialType::Basic)
25 .await?;
26 Ok(false)
27 }
28 Err(e) => Err(e),
29 Ok(_) => Ok(true),
30 }
31 }
32
33 pub async fn e2ei_verify_group_state(&self, group_info: VerifiableGroupInfo) -> Result<E2eiConversationState> {
35 self.crypto_provider
36 .authentication_service()
37 .refresh_time_of_interest()
38 .await;
39
40 let cs = group_info.ciphersuite().into();
41
42 let is_sender = true; let Ok(rt) = group_info.take_ratchet_tree(&self.crypto_provider, is_sender).await else {
44 return Ok(E2eiConversationState::NotVerified);
45 };
46
47 let credentials = rt.iter().filter_map(|n| match n {
48 Some(Node::LeafNode(ln)) => Some(ln.credential()),
49 _ => None,
50 });
51
52 Ok(Self::compute_conversation_state(
53 cs,
54 credentials,
55 MlsCredentialType::X509,
56 self.crypto_provider.authentication_service().borrow().await.as_ref(),
57 )
58 .await)
59 }
60
61 pub async fn get_credential_in_use(
64 &self,
65 group_info: VerifiableGroupInfo,
66 credential_type: MlsCredentialType,
67 ) -> Result<E2eiConversationState> {
68 let cs = group_info.ciphersuite().into();
69 let rt = group_info
74 .take_ratchet_tree(&self.crypto_provider, false)
75 .await
76 .map_err(MlsError::wrap("taking ratchet tree"))?;
77 Self::get_credential_in_use_in_ratchet_tree(
78 cs,
79 rt,
80 credential_type,
81 self.crypto_provider.authentication_service().borrow().await.as_ref(),
82 )
83 .await
84 }
85 pub(crate) async fn get_credential_in_use_in_ratchet_tree(
86 ciphersuite: MlsCiphersuite,
87 ratchet_tree: RatchetTree,
88 credential_type: MlsCredentialType,
89 env: Option<&wire_e2e_identity::prelude::x509::revocation::PkiEnvironment>,
90 ) -> Result<E2eiConversationState> {
91 let credentials = ratchet_tree.iter().filter_map(|n| match n {
92 Some(Node::LeafNode(ln)) => Some(ln.credential()),
93 _ => None,
94 });
95 Ok(Self::compute_conversation_state(ciphersuite, credentials, credential_type, env).await)
96 }
97
98 pub(crate) async fn compute_conversation_state<'a>(
101 ciphersuite: MlsCiphersuite,
102 credentials: impl Iterator<Item = &'a Credential>,
103 _credential_type: MlsCredentialType,
104 env: Option<&wire_e2e_identity::prelude::x509::revocation::PkiEnvironment>,
105 ) -> E2eiConversationState {
106 let mut is_e2ei = false;
107 let mut state = E2eiConversationState::Verified;
108
109 for credential in credentials {
110 let Ok(Some(cert)) = credential.parse_leaf_cert() else {
111 state = E2eiConversationState::NotVerified;
112 if is_e2ei {
113 break;
114 }
115 continue;
116 };
117
118 is_e2ei = true;
119
120 let invalid_identity = cert.extract_identity(env, ciphersuite.e2ei_hash_alg()).is_err();
121
122 use openmls_x509_credential::X509Ext as _;
123 let is_time_valid = cert.is_time_valid().unwrap_or(false);
124 let is_time_invalid = !is_time_valid;
125 let is_revoked_or_invalid = env
126 .map(|e| e.validate_cert_and_revocation(&cert).is_err())
127 .unwrap_or(false);
128
129 let is_invalid = invalid_identity || is_time_invalid || is_revoked_or_invalid;
130 if is_invalid {
131 state = E2eiConversationState::NotVerified;
132 break;
133 }
134 }
135
136 if is_e2ei {
137 state
138 } else {
139 E2eiConversationState::NotEnabled
140 }
141 }
142}