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