1use std::sync::Arc;
2
3use async_lock::RwLock;
4use core_crypto_keystore::Database;
5
6mod crypto_provider;
7mod error;
8#[cfg(test)]
9mod tests;
10
11pub(crate) use crypto_provider::CRYPTO;
12pub use crypto_provider::RustCrypto;
13pub(crate) use error::{Error, MlsProviderResult};
14use openmls_traits::{
15 authentication_service::{CredentialAuthenticationStatus, CredentialRef},
16 crypto::OpenMlsCrypto,
17 types::{
18 AeadType, Ciphersuite, CryptoError, ExporterSecret, HashType, HpkeCiphertext, HpkeConfig, HpkeKeyPair,
19 KemOutput, SignatureScheme,
20 },
21};
22use wire_e2e_identity::pki_env::PkiEnvironment;
23
24pub type RawEntropySeed = <rand_chacha::ChaCha20Rng as rand::SeedableRng>::Seed;
26
27#[derive(Debug, Clone, Default, PartialEq, Eq, zeroize::ZeroizeOnDrop)]
28#[repr(transparent)]
29pub struct EntropySeed(RawEntropySeed);
31
32impl EntropySeed {
33 pub const EXPECTED_LEN: usize = std::mem::size_of::<EntropySeed>() / std::mem::size_of::<u8>();
35
36 pub fn try_from_slice(data: &[u8]) -> MlsProviderResult<Self> {
47 if data.len() != Self::EXPECTED_LEN {
48 return Err(Error::EntropySeedLength {
49 actual: data.len(),
50 expected: Self::EXPECTED_LEN,
51 });
52 }
53
54 let mut inner = RawEntropySeed::default();
55 inner.copy_from_slice(data);
56
57 Ok(Self(inner))
58 }
59
60 pub fn from_raw(raw: RawEntropySeed) -> Self {
62 Self(raw)
63 }
64}
65
66impl std::ops::Deref for EntropySeed {
67 type Target = [u8];
68 fn deref(&self) -> &Self::Target {
69 &self.0
70 }
71}
72
73impl std::ops::DerefMut for EntropySeed {
74 fn deref_mut(&mut self) -> &mut Self::Target {
75 &mut self.0
76 }
77}
78
79#[derive(Debug)]
80pub struct AuthenticationService {
81 pki_env: RwLock<Option<Arc<PkiEnvironment>>>,
90}
91
92impl AuthenticationService {
93 pub async fn pki_env(&self) -> Option<Arc<PkiEnvironment>> {
94 self.pki_env.read().await.clone()
95 }
96}
97
98#[cfg_attr(target_os = "unknown", async_trait::async_trait(?Send))]
99#[cfg_attr(not(target_os = "unknown"), async_trait::async_trait)]
100impl openmls_traits::authentication_service::AuthenticationServiceDelegate for AuthenticationService {
101 async fn validate_credential<'a>(&'a self, credential: CredentialRef<'a>) -> CredentialAuthenticationStatus {
102 match credential {
103 CredentialRef::Basic { .. } => CredentialAuthenticationStatus::Valid,
105
106 CredentialRef::X509 { .. } => match self.pki_env.read().await.as_ref() {
107 None => {
108 log::warn!("unable to validate X509 credentials: PKI environment is unset");
109 CredentialAuthenticationStatus::Unknown
110 }
111 Some(pki_env) => {
112 use CredentialAuthenticationStatus::*;
113 match pki_env.validate_credential(credential).await {
116 Revoked => Valid,
117 Expired => Valid,
118 status => status,
119 }
120 }
121 },
122 }
123 }
124}
125
126#[derive(Debug, Clone)]
128pub struct CryptoProvider {
129 crypto: Arc<RustCrypto>,
130 key_store: Arc<Database>,
131 auth_service: Arc<AuthenticationService>,
132}
133
134impl CryptoProvider {
135 pub fn new(key_store: Arc<Database>) -> Self {
141 Self::new_with_pki_env(key_store, None)
142 }
143
144 pub fn new_with_pki_env(key_store: Arc<Database>, pki_env: Option<Arc<PkiEnvironment>>) -> Self {
146 let pki_env = RwLock::new(pki_env);
147 let auth_service = Arc::new(AuthenticationService { pki_env });
148 Self {
149 key_store,
150 crypto: Arc::clone(&CRYPTO),
151 auth_service,
152 }
153 }
154
155 pub async fn set_pki_environment(&mut self, pki_env: Option<Arc<PkiEnvironment>>) {
157 *self.auth_service.pki_env.write().await = pki_env;
158 }
159
160 pub async fn is_pki_env_setup(&self) -> bool {
162 self.auth_service.pki_env.read().await.is_some()
163 }
164
165 pub fn reseed(&self, entropy_seed: Option<EntropySeed>) -> MlsProviderResult<()> {
172 self.crypto.reseed(entropy_seed)
173 }
174
175 #[expect(clippy::too_many_arguments)]
177 pub fn hpke_seal_psk(
178 &self,
179 config: HpkeConfig,
180 pk_r: &[u8],
181 info: &[u8],
182 aad: &[u8],
183 psk: &[u8],
184 psk_id: &[u8],
185 ptxt: &[u8],
186 ) -> Result<HpkeCiphertext, CryptoError> {
187 self.crypto.hpke_seal_psk(config, pk_r, info, aad, psk, psk_id, ptxt)
188 }
189
190 #[expect(clippy::too_many_arguments)]
192 pub fn hpke_open_psk(
193 &self,
194 config: HpkeConfig,
195 input: &HpkeCiphertext,
196 sk_r: &[u8],
197 info: &[u8],
198 aad: &[u8],
199 psk: &[u8],
200 psk_id: &[u8],
201 ) -> Result<Vec<u8>, CryptoError> {
202 self.crypto.hpke_open_psk(config, input, sk_r, info, aad, psk, psk_id)
203 }
204}
205
206impl openmls_traits::OpenMlsCryptoProvider for CryptoProvider {
207 type CryptoProvider = RustCrypto;
208 type RandProvider = RustCrypto;
209 type KeyStoreProvider = Database;
210 type AuthenticationServiceProvider = AuthenticationService;
211
212 fn crypto(&self) -> &Self::CryptoProvider {
213 &self.crypto
214 }
215
216 fn rand(&self) -> &Self::RandProvider {
217 &self.crypto
218 }
219
220 fn key_store(&self) -> &Self::KeyStoreProvider {
221 &self.key_store
222 }
223
224 fn authentication_service(&self) -> &Self::AuthenticationServiceProvider {
225 &self.auth_service
226 }
227}
228
229impl OpenMlsCrypto for &CryptoProvider {
231 fn supports(&self, ciphersuite: Ciphersuite) -> Result<(), CryptoError> {
232 self.crypto.supports(ciphersuite)
233 }
234
235 fn supported_ciphersuites(&self) -> Vec<Ciphersuite> {
236 self.crypto.supported_ciphersuites()
237 }
238
239 fn hkdf_extract(
240 &self,
241 hash_type: HashType,
242 salt: &[u8],
243 ikm: &[u8],
244 ) -> Result<tls_codec::SecretVLBytes, CryptoError> {
245 self.crypto.hkdf_extract(hash_type, salt, ikm)
246 }
247
248 fn hkdf_expand(
249 &self,
250 hash_type: HashType,
251 prk: &[u8],
252 info: &[u8],
253 okm_len: usize,
254 ) -> Result<tls_codec::SecretVLBytes, CryptoError> {
255 self.crypto.hkdf_expand(hash_type, prk, info, okm_len)
256 }
257
258 fn hash(&self, hash_type: HashType, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
259 self.crypto.hash(hash_type, data)
260 }
261
262 fn aead_encrypt(
263 &self,
264 alg: AeadType,
265 key: &[u8],
266 data: &[u8],
267 nonce: &[u8],
268 aad: &[u8],
269 ) -> Result<Vec<u8>, CryptoError> {
270 self.crypto.aead_encrypt(alg, key, data, nonce, aad)
271 }
272
273 fn aead_decrypt(
274 &self,
275 alg: AeadType,
276 key: &[u8],
277 ct_tag: &[u8],
278 nonce: &[u8],
279 aad: &[u8],
280 ) -> Result<Vec<u8>, CryptoError> {
281 self.crypto.aead_decrypt(alg, key, ct_tag, nonce, aad)
282 }
283
284 fn signature_key_gen(&self, alg: SignatureScheme) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
285 self.crypto.signature_key_gen(alg)
286 }
287
288 fn signature_public_key_len(&self, alg: SignatureScheme) -> usize {
289 self.crypto.signature_public_key_len(alg)
290 }
291
292 fn validate_signature_key(&self, alg: SignatureScheme, key: &[u8]) -> Result<(), CryptoError> {
293 self.crypto.validate_signature_key(alg, key)
294 }
295
296 fn verify_signature(
297 &self,
298 alg: SignatureScheme,
299 data: &[u8],
300 pk: &[u8],
301 signature: &[u8],
302 ) -> Result<(), CryptoError> {
303 self.crypto.verify_signature(alg, data, pk, signature)
304 }
305
306 fn sign(&self, alg: SignatureScheme, data: &[u8], key: &[u8]) -> Result<Vec<u8>, CryptoError> {
307 self.crypto.sign(alg, data, key)
308 }
309
310 fn hpke_seal(
311 &self,
312 config: HpkeConfig,
313 pk_r: &[u8],
314 info: &[u8],
315 aad: &[u8],
316 ptxt: &[u8],
317 ) -> Result<HpkeCiphertext, CryptoError> {
318 self.crypto.hpke_seal(config, pk_r, info, aad, ptxt)
319 }
320
321 fn hpke_open(
322 &self,
323 config: HpkeConfig,
324 input: &HpkeCiphertext,
325 sk_r: &[u8],
326 info: &[u8],
327 aad: &[u8],
328 ) -> Result<Vec<u8>, CryptoError> {
329 self.crypto.hpke_open(config, input, sk_r, info, aad)
330 }
331
332 fn hpke_setup_sender_and_export(
333 &self,
334 config: HpkeConfig,
335 pk_r: &[u8],
336 info: &[u8],
337 exporter_context: &[u8],
338 exporter_length: usize,
339 ) -> Result<(KemOutput, ExporterSecret), CryptoError> {
340 self.crypto
341 .hpke_setup_sender_and_export(config, pk_r, info, exporter_context, exporter_length)
342 }
343
344 fn hpke_setup_receiver_and_export(
345 &self,
346 config: HpkeConfig,
347 enc: &[u8],
348 sk_r: &[u8],
349 info: &[u8],
350 exporter_context: &[u8],
351 exporter_length: usize,
352 ) -> Result<ExporterSecret, CryptoError> {
353 self.crypto
354 .hpke_setup_receiver_and_export(config, enc, sk_r, info, exporter_context, exporter_length)
355 }
356
357 fn derive_hpke_keypair(&self, config: HpkeConfig, ikm: &[u8]) -> Result<HpkeKeyPair, CryptoError> {
358 self.crypto.derive_hpke_keypair(config, ikm)
359 }
360}