mls_crypto_provider/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub use core_crypto_keystore::{Connection as CryptoKeystore, DatabaseKey};
4
5mod crypto_provider;
6mod error;
7mod pki;
8
9pub use error::{MlsProviderError, MlsProviderResult};
10
11pub use crypto_provider::RustCrypto;
12
13pub use pki::{CertProfile, CertificateGenerationArgs, PkiKeypair};
14
15use crate::pki::PkiEnvironmentProvider;
16
17pub mod reexports {
18    pub use rand_core;
19}
20
21/// 32-byte raw entropy seed
22pub type RawEntropySeed = <rand_chacha::ChaCha20Rng as rand::SeedableRng>::Seed;
23
24#[derive(Debug, Clone, Default, PartialEq, Eq, zeroize::ZeroizeOnDrop)]
25#[repr(transparent)]
26/// Wrapped 32-byte entropy seed with bounds check
27pub struct EntropySeed(RawEntropySeed);
28
29impl EntropySeed {
30    pub const EXPECTED_LEN: usize = std::mem::size_of::<EntropySeed>() / std::mem::size_of::<u8>();
31
32    pub fn try_from_slice(data: &[u8]) -> MlsProviderResult<Self> {
33        if data.len() < Self::EXPECTED_LEN {
34            return Err(MlsProviderError::EntropySeedLengthError {
35                actual: data.len(),
36                expected: Self::EXPECTED_LEN,
37            });
38        }
39
40        let mut inner = RawEntropySeed::default();
41        inner.copy_from_slice(&data[..Self::EXPECTED_LEN]);
42
43        Ok(Self(inner))
44    }
45
46    pub fn from_raw(raw: RawEntropySeed) -> Self {
47        Self(raw)
48    }
49}
50
51impl std::ops::Deref for EntropySeed {
52    type Target = [u8];
53    fn deref(&self) -> &Self::Target {
54        &self.0
55    }
56}
57
58impl std::ops::DerefMut for EntropySeed {
59    fn deref_mut(&mut self) -> &mut Self::Target {
60        &mut self.0
61    }
62}
63
64pub struct MlsCryptoProviderConfiguration<'a> {
65    /// File path or database name of the persistent storage
66    pub db_path: &'a str,
67    /// Encryption master key of the encrypted-at-rest persistent storage
68    pub db_key: DatabaseKey,
69    /// Dictates whether or not the backend storage is in memory or not
70    pub in_memory: bool,
71    /// External seed for the ChaCha20 PRNG entropy pool
72    pub entropy_seed: Option<EntropySeed>,
73}
74
75#[derive(Debug, Clone)]
76pub struct MlsCryptoProvider {
77    crypto: RustCrypto,
78    key_store: CryptoKeystore,
79    pki_env: PkiEnvironmentProvider,
80}
81
82impl MlsCryptoProvider {
83    /// Initialize a CryptoProvider with a backend following the provided `config` (see: [MlsCryptoProviderConfiguration])
84    pub async fn try_new_with_configuration(config: MlsCryptoProviderConfiguration<'_>) -> MlsProviderResult<Self> {
85        let crypto = config.entropy_seed.map(RustCrypto::new_with_seed).unwrap_or_default();
86        let key_store = if config.in_memory {
87            CryptoKeystore::open_in_memory_with_key("", &config.db_key).await?
88        } else {
89            CryptoKeystore::open_with_key(config.db_path, &config.db_key).await?
90        };
91        Ok(Self {
92            crypto,
93            key_store,
94            pki_env: PkiEnvironmentProvider::default(),
95        })
96    }
97
98    pub async fn try_new(db_path: impl AsRef<str>, db_key: &DatabaseKey) -> MlsProviderResult<Self> {
99        let crypto = RustCrypto::default();
100        let key_store = CryptoKeystore::open_with_key(db_path, db_key).await?;
101        Ok(Self {
102            crypto,
103            key_store,
104            pki_env: PkiEnvironmentProvider::default(),
105        })
106    }
107
108    pub async fn try_new_in_memory(db_key: &DatabaseKey) -> MlsProviderResult<Self> {
109        let crypto = RustCrypto::default();
110        let key_store = CryptoKeystore::open_in_memory_with_key("", db_key).await?;
111        Ok(Self {
112            crypto,
113            key_store,
114            pki_env: PkiEnvironmentProvider::default(),
115        })
116    }
117
118    /// Initialize a CryptoProvided with an already-configured backing store
119    pub fn new_with_store(key_store: CryptoKeystore, entropy_seed: Option<EntropySeed>) -> Self {
120        let crypto = entropy_seed.map(RustCrypto::new_with_seed).unwrap_or_default();
121        Self {
122            crypto,
123            key_store,
124            pki_env: PkiEnvironmentProvider::default(),
125        }
126    }
127
128    /// Clones the references of the PkiEnvironment and the CryptoProvider into a transaction
129    /// keystore to pass to openmls as the `OpenMlsCryptoProvider`
130    pub async fn new_transaction(&self) -> MlsProviderResult<()> {
131        self.key_store.new_transaction().await.map_err(Into::into)
132    }
133
134    /// Replaces the PKI env currently in place
135    pub async fn update_pki_env(
136        &self,
137        pki_env: wire_e2e_identity::prelude::x509::revocation::PkiEnvironment,
138    ) -> MlsProviderResult<()> {
139        self.pki_env.update_env(pki_env).await
140    }
141
142    /// Returns whether we have a PKI env setup
143    pub async fn is_pki_env_setup(&self) -> bool {
144        self.pki_env.is_env_setup().await
145    }
146
147    /// Reseeds the internal CSPRNG entropy pool with a brand new one.
148    ///
149    /// If [None] is provided, the new entropy will be pulled through the current OS target's capabilities
150    pub fn reseed(&self, entropy_seed: Option<EntropySeed>) -> MlsProviderResult<()> {
151        self.crypto.reseed(entropy_seed)
152    }
153
154    /// Returns whether or not it is currently possible to close this provider.
155    ///
156    /// Reasons why it may not currently be possible:
157    ///
158    /// - A transaction is currently in progress
159    /// - Multiple strong references currently exist to the keystore
160    ///
161    /// As with all such checks, this is vulnerable to TOCTOU issues, but as the current implementation
162    /// of the [`close`] function consumes `self`, this is the only way to check in advance whether
163    /// this will in principle work.
164    pub async fn can_close(&self) -> bool {
165        self.key_store.can_close().await
166    }
167
168    /// Closes this provider, which in turns tears down the backing store
169    ///
170    /// Note: This does **not** destroy the data on-disk in case of persistent backing store
171    pub async fn close(self) -> MlsProviderResult<()> {
172        self.key_store.close().await.map_err(Into::into)
173    }
174
175    /// Clone keystore (its an `Arc` internnaly)
176    pub fn keystore(&self) -> CryptoKeystore {
177        self.key_store.clone()
178    }
179
180    /// Allows to retrieve the underlying key store directly
181    pub fn unwrap_keystore(self) -> CryptoKeystore {
182        self.key_store
183    }
184}
185
186impl openmls_traits::OpenMlsCryptoProvider for MlsCryptoProvider {
187    type CryptoProvider = RustCrypto;
188    type RandProvider = RustCrypto;
189    type KeyStoreProvider = CryptoKeystore;
190    type AuthenticationServiceProvider = PkiEnvironmentProvider;
191
192    fn crypto(&self) -> &Self::CryptoProvider {
193        &self.crypto
194    }
195
196    fn rand(&self) -> &Self::RandProvider {
197        &self.crypto
198    }
199
200    fn key_store(&self) -> &Self::KeyStoreProvider {
201        &self.key_store
202    }
203
204    fn authentication_service(&self) -> &Self::AuthenticationServiceProvider {
205        &self.pki_env
206    }
207}