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<()> {
169 self.crypto.reseed(entropy_seed)
170 }
171
172 #[expect(clippy::too_many_arguments)]
174 pub fn hpke_seal_psk(
175 &self,
176 config: HpkeConfig,
177 pk_r: &[u8],
178 info: &[u8],
179 aad: &[u8],
180 psk: &[u8],
181 psk_id: &[u8],
182 ptxt: &[u8],
183 ) -> Result<HpkeCiphertext, CryptoError> {
184 self.crypto.hpke_seal_psk(config, pk_r, info, aad, psk, psk_id, ptxt)
185 }
186
187 #[expect(clippy::too_many_arguments)]
189 pub fn hpke_open_psk(
190 &self,
191 config: HpkeConfig,
192 input: &HpkeCiphertext,
193 sk_r: &[u8],
194 info: &[u8],
195 aad: &[u8],
196 psk: &[u8],
197 psk_id: &[u8],
198 ) -> Result<Vec<u8>, CryptoError> {
199 self.crypto.hpke_open_psk(config, input, sk_r, info, aad, psk, psk_id)
200 }
201}
202
203impl openmls_traits::OpenMlsCryptoProvider for CryptoProvider {
204 type CryptoProvider = RustCrypto;
205 type RandProvider = RustCrypto;
206 type KeyStoreProvider = Database;
207 type AuthenticationServiceProvider = AuthenticationService;
208
209 fn crypto(&self) -> &Self::CryptoProvider {
210 &self.crypto
211 }
212
213 fn rand(&self) -> &Self::RandProvider {
214 &self.crypto
215 }
216
217 fn key_store(&self) -> &Self::KeyStoreProvider {
218 &self.key_store
219 }
220
221 fn authentication_service(&self) -> &Self::AuthenticationServiceProvider {
222 &self.auth_service
223 }
224}
225
226impl OpenMlsCrypto for &CryptoProvider {
228 fn supports(&self, ciphersuite: Ciphersuite) -> Result<(), CryptoError> {
229 self.crypto.supports(ciphersuite)
230 }
231
232 fn supported_ciphersuites(&self) -> Vec<Ciphersuite> {
233 self.crypto.supported_ciphersuites()
234 }
235
236 fn hkdf_extract(
237 &self,
238 hash_type: HashType,
239 salt: &[u8],
240 ikm: &[u8],
241 ) -> Result<tls_codec::SecretVLBytes, CryptoError> {
242 self.crypto.hkdf_extract(hash_type, salt, ikm)
243 }
244
245 fn hkdf_expand(
246 &self,
247 hash_type: HashType,
248 prk: &[u8],
249 info: &[u8],
250 okm_len: usize,
251 ) -> Result<tls_codec::SecretVLBytes, CryptoError> {
252 self.crypto.hkdf_expand(hash_type, prk, info, okm_len)
253 }
254
255 fn hash(&self, hash_type: HashType, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
256 self.crypto.hash(hash_type, data)
257 }
258
259 fn aead_encrypt(
260 &self,
261 alg: AeadType,
262 key: &[u8],
263 data: &[u8],
264 nonce: &[u8],
265 aad: &[u8],
266 ) -> Result<Vec<u8>, CryptoError> {
267 self.crypto.aead_encrypt(alg, key, data, nonce, aad)
268 }
269
270 fn aead_decrypt(
271 &self,
272 alg: AeadType,
273 key: &[u8],
274 ct_tag: &[u8],
275 nonce: &[u8],
276 aad: &[u8],
277 ) -> Result<Vec<u8>, CryptoError> {
278 self.crypto.aead_decrypt(alg, key, ct_tag, nonce, aad)
279 }
280
281 fn signature_key_gen(&self, alg: SignatureScheme) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
282 self.crypto.signature_key_gen(alg)
283 }
284
285 fn signature_public_key_len(&self, alg: SignatureScheme) -> usize {
286 self.crypto.signature_public_key_len(alg)
287 }
288
289 fn validate_signature_key(&self, alg: SignatureScheme, key: &[u8]) -> Result<(), CryptoError> {
290 self.crypto.validate_signature_key(alg, key)
291 }
292
293 fn verify_signature(
294 &self,
295 alg: SignatureScheme,
296 data: &[u8],
297 pk: &[u8],
298 signature: &[u8],
299 ) -> Result<(), CryptoError> {
300 self.crypto.verify_signature(alg, data, pk, signature)
301 }
302
303 fn sign(&self, alg: SignatureScheme, data: &[u8], key: &[u8]) -> Result<Vec<u8>, CryptoError> {
304 self.crypto.sign(alg, data, key)
305 }
306
307 fn hpke_seal(
308 &self,
309 config: HpkeConfig,
310 pk_r: &[u8],
311 info: &[u8],
312 aad: &[u8],
313 ptxt: &[u8],
314 ) -> Result<HpkeCiphertext, CryptoError> {
315 self.crypto.hpke_seal(config, pk_r, info, aad, ptxt)
316 }
317
318 fn hpke_open(
319 &self,
320 config: HpkeConfig,
321 input: &HpkeCiphertext,
322 sk_r: &[u8],
323 info: &[u8],
324 aad: &[u8],
325 ) -> Result<Vec<u8>, CryptoError> {
326 self.crypto.hpke_open(config, input, sk_r, info, aad)
327 }
328
329 fn hpke_setup_sender_and_export(
330 &self,
331 config: HpkeConfig,
332 pk_r: &[u8],
333 info: &[u8],
334 exporter_context: &[u8],
335 exporter_length: usize,
336 ) -> Result<(KemOutput, ExporterSecret), CryptoError> {
337 self.crypto
338 .hpke_setup_sender_and_export(config, pk_r, info, exporter_context, exporter_length)
339 }
340
341 fn hpke_setup_receiver_and_export(
342 &self,
343 config: HpkeConfig,
344 enc: &[u8],
345 sk_r: &[u8],
346 info: &[u8],
347 exporter_context: &[u8],
348 exporter_length: usize,
349 ) -> Result<ExporterSecret, CryptoError> {
350 self.crypto
351 .hpke_setup_receiver_and_export(config, enc, sk_r, info, exporter_context, exporter_length)
352 }
353
354 fn derive_hpke_keypair(&self, config: HpkeConfig, ikm: &[u8]) -> Result<HpkeKeyPair, CryptoError> {
355 self.crypto.derive_hpke_keypair(config, ikm)
356 }
357}