core_crypto/mls_provider/
mod.rs

1// TODO: remove this expect(unreachable_pub) once the E2EI parts have been coupled.
2#![expect(unreachable_pub)]
3use std::sync::Arc;
4
5pub use core_crypto_keystore::{Database, DatabaseKey};
6
7mod crypto_provider;
8mod error;
9
10pub(crate) use crypto_provider::CRYPTO;
11pub use crypto_provider::RustCrypto;
12pub use error::{MlsProviderError, MlsProviderResult};
13use openmls_traits::{
14    crypto::OpenMlsCrypto,
15    types::{
16        AeadType, Ciphersuite, CryptoError, ExporterSecret, HashType, HpkeCiphertext, HpkeConfig, HpkeKeyPair,
17        KemOutput, SignatureScheme,
18    },
19};
20use wire_e2e_identity::PkiEnvironmentProvider;
21// TODO: remove this allow(unused) once the E2EI parts have been coupled.
22#[allow(unused)]
23pub use wire_e2e_identity::pki::{CertProfile, CertificateGenerationArgs, PkiKeypair};
24
25/// 32-byte raw entropy seed
26pub type RawEntropySeed = <rand_chacha::ChaCha20Rng as rand::SeedableRng>::Seed;
27
28#[derive(Debug, Clone, Default, PartialEq, Eq, zeroize::ZeroizeOnDrop)]
29#[repr(transparent)]
30/// Wrapped 32-byte entropy seed with bounds check
31pub struct EntropySeed(RawEntropySeed);
32
33impl EntropySeed {
34    /// The expected length of the entopy seed, in bytes.
35    pub const EXPECTED_LEN: usize = std::mem::size_of::<EntropySeed>() / std::mem::size_of::<u8>();
36
37    /// Create an entropy seed from the provided slice.
38    pub fn try_from_slice(data: &[u8]) -> MlsProviderResult<Self> {
39        if data.len() < Self::EXPECTED_LEN {
40            return Err(MlsProviderError::EntropySeedLengthError {
41                actual: data.len(),
42                expected: Self::EXPECTED_LEN,
43            });
44        }
45
46        let mut inner = RawEntropySeed::default();
47        inner.copy_from_slice(&data[..Self::EXPECTED_LEN]);
48
49        Ok(Self(inner))
50    }
51
52    /// Create an entropy seed from the provided raw entropy seed.
53    pub fn from_raw(raw: RawEntropySeed) -> Self {
54        Self(raw)
55    }
56}
57
58impl std::ops::Deref for EntropySeed {
59    type Target = [u8];
60    fn deref(&self) -> &Self::Target {
61        &self.0
62    }
63}
64
65impl std::ops::DerefMut for EntropySeed {
66    fn deref_mut(&mut self) -> &mut Self::Target {
67        &mut self.0
68    }
69}
70
71/// The MLS crypto provider
72#[derive(Debug, Clone)]
73pub struct MlsCryptoProvider {
74    crypto: Arc<RustCrypto>,
75    key_store: Database,
76    pki_env: PkiEnvironmentProvider,
77}
78
79impl MlsCryptoProvider {
80    /// Construct a crypto provider with defaults and a given [Database].
81    ///
82    /// See also:
83    ///
84    /// - [Database::open]
85    pub fn new(key_store: Database) -> Self {
86        Self {
87            key_store,
88            crypto: Arc::clone(&CRYPTO),
89            pki_env: Default::default(),
90        }
91    }
92
93    /// Construct a crypto provider with the given database and the PKI environment.
94    pub fn new_with_pki_env(key_store: Database, pki_env: PkiEnvironmentProvider) -> Self {
95        Self {
96            key_store,
97            crypto: Arc::clone(&CRYPTO),
98            pki_env,
99        }
100    }
101
102    /// Clones the references of the PkiEnvironment and the CryptoProvider into a transaction
103    /// keystore to pass to openmls as the `OpenMlsCryptoProvider`
104    pub async fn new_transaction(&self) -> MlsProviderResult<()> {
105        self.key_store.new_transaction().await.map_err(Into::into)
106    }
107
108    /// Replaces the PKI env currently in place
109    pub async fn update_pki_env(&self, pki_env: Option<wire_e2e_identity::x509_check::revocation::PkiEnvironment>) {
110        self.pki_env.update_env(pki_env).await
111    }
112
113    /// Set pki_env to a new shared pki environment provider
114    pub async fn set_pki_environment_provider(&mut self, pki_env: Option<PkiEnvironmentProvider>) {
115        if let Some(pki_env) = pki_env {
116            self.pki_env = pki_env;
117        } else {
118            self.pki_env.update_env(None).await;
119        }
120    }
121
122    /// Returns whether we have a PKI env setup
123    pub async fn is_pki_env_setup(&self) -> bool {
124        self.pki_env.is_env_setup().await
125    }
126
127    /// Reseeds the internal CSPRNG entropy pool with a brand new one.
128    ///
129    /// If [None] is provided, the new entropy will be pulled through the current OS target's capabilities
130    pub fn reseed(&self, entropy_seed: Option<EntropySeed>) -> MlsProviderResult<()> {
131        self.crypto.reseed(entropy_seed)
132    }
133
134    /// Wait for any keystore transaction to finish, then close the database connection.
135    ///
136    /// Note: This does **not** destroy the data on-disk in case of persistent backing store
137    pub async fn close(&self) -> MlsProviderResult<()> {
138        self.key_store.close().await?;
139        Ok(())
140    }
141}
142
143impl openmls_traits::OpenMlsCryptoProvider for MlsCryptoProvider {
144    type CryptoProvider = RustCrypto;
145    type RandProvider = RustCrypto;
146    type KeyStoreProvider = Database;
147    type AuthenticationServiceProvider = PkiEnvironmentProvider;
148
149    fn crypto(&self) -> &Self::CryptoProvider {
150        &self.crypto
151    }
152
153    fn rand(&self) -> &Self::RandProvider {
154        &self.crypto
155    }
156
157    fn key_store(&self) -> &Self::KeyStoreProvider {
158        &self.key_store
159    }
160
161    fn authentication_service(&self) -> &Self::AuthenticationServiceProvider {
162        &self.pki_env
163    }
164}
165
166/// Passthrough implementation of crypto functionality for references to `MlsCryptoProvider`.
167impl OpenMlsCrypto for &MlsCryptoProvider {
168    fn supports(&self, ciphersuite: Ciphersuite) -> Result<(), CryptoError> {
169        self.crypto.supports(ciphersuite)
170    }
171
172    fn supported_ciphersuites(&self) -> Vec<Ciphersuite> {
173        self.crypto.supported_ciphersuites()
174    }
175
176    fn hkdf_extract(
177        &self,
178        hash_type: HashType,
179        salt: &[u8],
180        ikm: &[u8],
181    ) -> Result<tls_codec::SecretVLBytes, CryptoError> {
182        self.crypto.hkdf_extract(hash_type, salt, ikm)
183    }
184
185    fn hkdf_expand(
186        &self,
187        hash_type: HashType,
188        prk: &[u8],
189        info: &[u8],
190        okm_len: usize,
191    ) -> Result<tls_codec::SecretVLBytes, CryptoError> {
192        self.crypto.hkdf_expand(hash_type, prk, info, okm_len)
193    }
194
195    fn hash(&self, hash_type: HashType, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
196        self.crypto.hash(hash_type, data)
197    }
198
199    fn aead_encrypt(
200        &self,
201        alg: AeadType,
202        key: &[u8],
203        data: &[u8],
204        nonce: &[u8],
205        aad: &[u8],
206    ) -> Result<Vec<u8>, CryptoError> {
207        self.crypto.aead_encrypt(alg, key, data, nonce, aad)
208    }
209
210    fn aead_decrypt(
211        &self,
212        alg: AeadType,
213        key: &[u8],
214        ct_tag: &[u8],
215        nonce: &[u8],
216        aad: &[u8],
217    ) -> Result<Vec<u8>, CryptoError> {
218        self.crypto.aead_decrypt(alg, key, ct_tag, nonce, aad)
219    }
220
221    fn signature_key_gen(&self, alg: SignatureScheme) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
222        self.crypto.signature_key_gen(alg)
223    }
224
225    fn signature_public_key_len(&self, alg: SignatureScheme) -> usize {
226        self.crypto.signature_public_key_len(alg)
227    }
228
229    fn validate_signature_key(&self, alg: SignatureScheme, key: &[u8]) -> Result<(), CryptoError> {
230        self.crypto.validate_signature_key(alg, key)
231    }
232
233    fn verify_signature(
234        &self,
235        alg: SignatureScheme,
236        data: &[u8],
237        pk: &[u8],
238        signature: &[u8],
239    ) -> Result<(), CryptoError> {
240        self.crypto.verify_signature(alg, data, pk, signature)
241    }
242
243    fn sign(&self, alg: SignatureScheme, data: &[u8], key: &[u8]) -> Result<Vec<u8>, CryptoError> {
244        self.crypto.sign(alg, data, key)
245    }
246
247    fn hpke_seal(
248        &self,
249        config: HpkeConfig,
250        pk_r: &[u8],
251        info: &[u8],
252        aad: &[u8],
253        ptxt: &[u8],
254    ) -> Result<HpkeCiphertext, CryptoError> {
255        self.crypto.hpke_seal(config, pk_r, info, aad, ptxt)
256    }
257
258    fn hpke_open(
259        &self,
260        config: HpkeConfig,
261        input: &HpkeCiphertext,
262        sk_r: &[u8],
263        info: &[u8],
264        aad: &[u8],
265    ) -> Result<Vec<u8>, CryptoError> {
266        self.crypto.hpke_open(config, input, sk_r, info, aad)
267    }
268
269    fn hpke_setup_sender_and_export(
270        &self,
271        config: HpkeConfig,
272        pk_r: &[u8],
273        info: &[u8],
274        exporter_context: &[u8],
275        exporter_length: usize,
276    ) -> Result<(KemOutput, ExporterSecret), CryptoError> {
277        self.crypto
278            .hpke_setup_sender_and_export(config, pk_r, info, exporter_context, exporter_length)
279    }
280
281    fn hpke_setup_receiver_and_export(
282        &self,
283        config: HpkeConfig,
284        enc: &[u8],
285        sk_r: &[u8],
286        info: &[u8],
287        exporter_context: &[u8],
288        exporter_length: usize,
289    ) -> Result<ExporterSecret, CryptoError> {
290        self.crypto
291            .hpke_setup_receiver_and_export(config, enc, sk_r, info, exporter_context, exporter_length)
292    }
293
294    fn derive_hpke_keypair(&self, config: HpkeConfig, ikm: &[u8]) -> Result<HpkeKeyPair, CryptoError> {
295        self.crypto.derive_hpke_keypair(config, ikm)
296    }
297}