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