Skip to main content

core_crypto/
lib.rs

1//! Core Crypto is a wrapper on top of OpenMLS aimed to provide an ergonomic API for usage in web
2//! through Web Assembly and in mobile devices through FFI.
3//!
4//! The goal is provide a easier and less verbose API to create, manage and interact with MLS
5//! groups.
6#![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;
13// both imports above have to be defined at the beginning of the crate for rstest to work
14
15mod build_metadata;
16mod bytes_wrapper;
17mod ephemeral;
18mod error;
19mod identity;
20mod immutable_database;
21
22/// MLS Abstraction
23pub mod mls;
24mod mls_provider;
25/// Proteus Abstraction
26#[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(crate) use bytes_wrapper::bytes_wrapper;
36pub use core_crypto_keystore::{ConnectionType, Database, DatabaseKey};
37#[cfg(test)]
38pub use core_crypto_macros::{dispotent, durable, idempotent};
39pub(crate) use immutable_database::ImmutableDatabase;
40pub use openmls::{
41    group::{MlsGroup, MlsGroupConfig},
42    prelude::{
43        Ciphersuite as MlsCiphersuite, GroupEpoch, KeyPackageIn, MlsMessageIn, MlsMessageInBody, Node, SignatureScheme,
44        group_info::VerifiableGroupInfo,
45    },
46};
47use wire_e2e_identity::{legacy::device_status::DeviceStatus, pki_env::PkiEnvironment};
48
49pub use crate::{
50    build_metadata::{BUILD_METADATA, BuildMetadata},
51    ephemeral::{HISTORY_CLIENT_ID_PREFIX, HistorySecret},
52    error::{
53        Error, InnermostErrorMessage, KeystoreError, LeafError, OpenMlsError, OpenMlsErrorKind, ProteusError,
54        ProteusErrorKind, RecursiveError, Result, ToRecursiveError,
55    },
56    identity::{WireIdentity, X509Identity},
57    mls::{
58        ExternalSender,
59        cipher_suite::CipherSuite,
60        conversation::{
61            BufferedDecryptedMessage, CommitBundle, ConversationConfiguration, ConversationId, CustomConfiguration,
62            DecryptedMessage, GroupInfoBundle, GroupInfoEncryptionType, GroupInfoPayload, RatchetTreeType, WirePolicy,
63        },
64        credential::{
65            Credential, CredentialRef, CredentialType, FindFilters as CredentialFindFilters, x509::CertificateBundle,
66        },
67        key_package::{Keypackage, KeypackageRef},
68        session::{
69            EpochObserver, HistoryObserver, Session,
70            id::{ClientId, ClientIdRef},
71            identifier::ClientIdentifier,
72            user_id::UserId,
73        },
74    },
75    mls_provider::{CryptoProvider, EntropySeed, RawEntropySeed, RustCrypto},
76    transaction_context::{
77        e2e_identity::conversation_state::E2eiConversationState, key_package::KEYPACKAGE_DEFAULT_LIFETIME,
78    },
79};
80
81/// An entity / data which has been packaged by the application to be encrypted
82/// and transmitted in an application message.
83#[derive(Debug, derive_more::From, derive_more::Deref, serde::Serialize, serde::Deserialize)]
84pub struct TransportData(pub Vec<u8>);
85
86/// Client callbacks to allow communication with the delivery service.
87/// There are two different endpoints, one for messages and one for commit bundles.
88#[cfg_attr(target_os = "unknown", async_trait::async_trait(?Send))]
89#[cfg_attr(not(target_os = "unknown"), async_trait::async_trait)]
90pub trait MlsTransport: std::fmt::Debug + Send + Sync {
91    /// Send a commit bundle to the corresponding endpoint.
92    async fn send_commit_bundle(&self, commit_bundle: CommitBundle) -> Result<()>;
93
94    /// This function will be called before a history secret is sent to the mls transport to allow
95    /// the application to package it in a suitable transport container (json, protobuf, ...).
96    ///
97    /// The `secret` parameter contain the history client's secrets which will be sent over the mls transport.
98    ///
99    /// Returns the history secret packaged for transport
100    async fn prepare_for_transport(&self, secret: &HistorySecret) -> Result<TransportData>;
101}
102
103/// This provider is mainly used for the initialization of the history client session, the only case where transport
104/// doesn't need to be implemented.
105#[derive(Debug, Default)]
106pub struct CoreCryptoTransportNotImplementedProvider();
107
108#[cfg_attr(target_os = "unknown", async_trait::async_trait(?Send))]
109#[cfg_attr(not(target_os = "unknown"), async_trait::async_trait)]
110impl MlsTransport for CoreCryptoTransportNotImplementedProvider {
111    async fn send_commit_bundle(&self, _commit_bundle: CommitBundle) -> crate::Result<()> {
112        Err(Error::MlsTransportNotProvided)
113    }
114
115    async fn prepare_for_transport(&self, _secret: &HistorySecret) -> crate::Result<TransportData> {
116        Err(Error::MlsTransportNotProvided)
117    }
118}
119
120/// Wrapper superstruct for both [mls::session::Session] and [proteus::ProteusCentral]
121///
122/// As [std::ops::Deref] is implemented, this struct is automatically dereferred to [mls::session::Session] apart from
123/// `proteus_*` calls
124#[derive(Debug)]
125pub struct CoreCrypto {
126    database: Arc<Database>,
127    pki_environment: RwLock<Option<Arc<PkiEnvironment>>>,
128    mls: RwLock<Option<mls::session::Session>>,
129    #[cfg(feature = "proteus")]
130    proteus: Mutex<Option<proteus::ProteusCentral>>,
131    #[cfg(not(feature = "proteus"))]
132    #[allow(dead_code)]
133    proteus: (),
134}
135
136impl CoreCrypto {
137    /// Create an new CoreCrypto client without any initialized session.
138    pub fn new(database: Arc<Database>) -> Arc<Self> {
139        Self {
140            database,
141            pki_environment: Default::default(),
142            mls: Default::default(),
143            proteus: Default::default(),
144        }
145        .into()
146    }
147
148    /// Set the session's PKI Environment
149    pub async fn set_pki_environment(&self, pki_environment: Option<Arc<PkiEnvironment>>) {
150        *self.pki_environment.write().await = pki_environment.clone();
151        if let Some(mls_session) = self.mls.write().await.as_mut() {
152            mls_session.crypto_provider.set_pki_environment(pki_environment).await;
153        }
154    }
155
156    /// Get the session's PKI Environment
157    pub async fn get_pki_environment(&self) -> Option<Arc<PkiEnvironment>> {
158        self.pki_environment.read().await.clone()
159    }
160
161    /// Get the mls session if initialized
162    pub async fn mls_session(&self) -> Result<Session> {
163        if let Some(session) = self.mls.read().await.as_ref() {
164            return Ok(session.clone());
165        }
166        let err = Err(mls::session::Error::MlsNotInitialized);
167        err.map_err(RecursiveError::mls_client("Getting mls session"))?
168    }
169
170    /// Get the database
171    pub fn database(&self) -> ImmutableDatabase {
172        self.database.clone().into()
173    }
174}