mls_crypto_provider/
lib.rs

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