1#![doc = include_str!(env!("STRIPPED_README_PATH"))]
7#![cfg_attr(not(test), deny(missing_docs))]
8#![allow(clippy::single_component_path_imports)]
9
10#[cfg(test)]
11#[macro_use]
12pub mod test_utils;
13mod build_metadata;
16mod ephemeral;
17mod error;
18mod group_store;
19
20pub mod e2e_identity;
22pub mod mls;
24mod mls_provider;
25#[cfg(feature = "proteus")]
27pub mod proteus;
28pub mod transaction_context;
29
30use std::sync::Arc;
31
32#[cfg(feature = "proteus")]
33use async_lock::Mutex;
34use async_lock::RwLock;
35pub use core_crypto_keystore::{ConnectionType, Database, DatabaseKey};
36#[cfg(test)]
37pub use core_crypto_macros::{dispotent, durable, idempotent};
38pub use openmls::{
39 group::{MlsGroup, MlsGroupConfig},
40 prelude::{
41 Ciphersuite as MlsCiphersuite, GroupEpoch, KeyPackageIn, MlsMessageIn, Node, SignatureScheme,
42 group_info::VerifiableGroupInfo,
43 },
44};
45use wire_e2e_identity::pki_env::PkiEnvironment;
46
47pub use crate::{
48 build_metadata::{BUILD_METADATA, BuildMetadata},
49 e2e_identity::{
50 E2eiEnrollment,
51 device_status::DeviceStatus,
52 identity::{WireIdentity, X509Identity},
53 types::{E2eiAcmeChallenge, E2eiAcmeDirectory, E2eiNewAcmeAuthz, E2eiNewAcmeOrder},
54 },
55 ephemeral::{HISTORY_CLIENT_ID_PREFIX, HistorySecret},
56 error::{
57 Error, InnermostErrorMessage, KeystoreError, LeafError, MlsError, MlsErrorKind, ProteusError, ProteusErrorKind,
58 RecursiveError, Result, ToRecursiveError,
59 },
60 mls::{
61 ciphersuite::Ciphersuite,
62 conversation::{
63 ConversationId, MlsConversation,
64 commit::MlsCommitBundle,
65 config::{MlsConversationConfiguration, MlsCustomConfiguration, MlsWirePolicy},
66 conversation_guard::decrypt::{MlsBufferedConversationDecryptMessage, MlsConversationDecryptMessage},
67 group_info::{GroupInfoPayload, MlsGroupInfoBundle, MlsGroupInfoEncryptionType, MlsRatchetTreeType},
68 proposal::MlsProposalBundle,
69 welcome::WelcomeBundle,
70 },
71 credential::{
72 Credential, CredentialRef, CredentialType, FindFilters as CredentialFindFilters, x509::CertificateBundle,
73 },
74 key_package::{Keypackage, KeypackageRef},
75 proposal::{MlsProposal, MlsProposalRef},
76 session::{
77 EpochObserver, HistoryObserver, Session,
78 id::{ClientId, ClientIdRef},
79 identifier::ClientIdentifier,
80 user_id::UserId,
81 },
82 },
83 mls_provider::{EntropySeed, MlsCryptoProvider, RawEntropySeed, RustCrypto},
84 transaction_context::{
85 e2e_identity::conversation_state::E2eiConversationState, key_package::KEYPACKAGE_DEFAULT_LIFETIME,
86 },
87};
88
89pub enum MlsTransportResponse {
91 Success,
93 Retry,
95 Abort {
97 reason: String,
99 },
100}
101
102#[derive(Debug, derive_more::From, derive_more::Deref, serde::Serialize, serde::Deserialize)]
105pub struct MlsTransportData(pub Vec<u8>);
106
107#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
110#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
111pub trait MlsTransport: std::fmt::Debug + Send + Sync {
112 async fn send_commit_bundle(&self, commit_bundle: MlsCommitBundle) -> Result<MlsTransportResponse>;
114 async fn send_message(&self, mls_message: Vec<u8>) -> Result<MlsTransportResponse>;
116
117 async fn prepare_for_transport(&self, secret: &HistorySecret) -> Result<MlsTransportData>;
124}
125
126#[derive(Debug, Default)]
129pub struct CoreCryptoTransportNotImplementedProvider();
130
131#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
132#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
133impl MlsTransport for CoreCryptoTransportNotImplementedProvider {
134 async fn send_commit_bundle(&self, _commit_bundle: MlsCommitBundle) -> crate::Result<MlsTransportResponse> {
135 Err(Error::MlsTransportNotProvided)
136 }
137
138 async fn send_message(&self, _mls_message: Vec<u8>) -> crate::Result<MlsTransportResponse> {
139 Err(Error::MlsTransportNotProvided)
140 }
141
142 async fn prepare_for_transport(&self, _secret: &HistorySecret) -> crate::Result<MlsTransportData> {
143 Err(Error::MlsTransportNotProvided)
144 }
145}
146
147#[derive(Debug, Clone)]
154pub struct CoreCrypto {
155 database: Database,
156 pki_environment: Arc<RwLock<Option<PkiEnvironment>>>,
157 mls: Arc<RwLock<Option<mls::session::Session<Database>>>>,
158 #[cfg(feature = "proteus")]
159 proteus: Arc<Mutex<Option<proteus::ProteusCentral>>>,
160 #[cfg(not(feature = "proteus"))]
161 #[allow(dead_code)]
162 proteus: (),
163}
164
165impl CoreCrypto {
166 pub fn new(database: Database) -> Self {
168 Self {
169 database,
170 pki_environment: Default::default(),
171 mls: Default::default(),
172 proteus: Default::default(),
173 }
174 }
175
176 pub async fn set_pki_environment(&self, pki_environment: Option<PkiEnvironment>) -> Result<()> {
178 if let Some(mls_session) = self.mls.write().await.as_mut() {
179 mls_session
180 .crypto_provider
181 .set_pki_environment_provider(pki_environment.as_ref().map(|p| p.mls_pki_env_provider()))
182 .await
183 }
184
185 let mut guard = self.pki_environment.write().await;
186 *guard = pki_environment;
187
188 Ok(())
189 }
190
191 pub async fn get_pki_environment(&self) -> Option<PkiEnvironment> {
193 self.pki_environment.read().await.clone()
194 }
195
196 pub async fn mls_session(&self) -> Result<Session<Database>> {
198 if let Some(session) = self.mls.read().await.as_ref() {
199 return Ok(session.clone());
200 }
201 let err = Err(mls::session::Error::MlsNotInitialized);
202 err.map_err(RecursiveError::mls_client("Getting mls session"))?
203 }
204
205 pub fn database(&self) -> Database {
207 self.database.clone()
208 }
209
210 pub async fn close(&self) -> Result<()> {
213 self.database
214 .close()
215 .await
216 .map_err(crate::KeystoreError::wrap("Closing database"))?;
217
218 if let Some(pki_env) = self.get_pki_environment().await {
219 pki_env
220 .database()
221 .close()
222 .await
223 .map_err(crate::KeystoreError::wrap("Closing PKI environment database"))?;
224 }
225
226 Ok(())
227 }
228}