Skip to main content

openmls/test_utils/
restricted_provider.rs

1//! A test provider that restricts the set of supported ciphersuites.
2//!
3//! [`RestrictedProvider`] delegates all crypto operations to [`RustCrypto`]
4//! but overrides [`OpenMlsCrypto::supports`] and
5//! [`OpenMlsCrypto::supported_ciphersuites`] to an explicit allowlist. This
6//! makes it possible to test the library's unsupported-ciphersuite error
7//! paths, which cannot be reached with the regular providers (they support
8//! every ciphersuite the tests can pick).
9
10use openmls_rust_crypto::{MemoryStorage, OpenMlsRustCrypto, RustCrypto};
11use openmls_traits::{
12    crypto::OpenMlsCrypto,
13    types::{
14        AeadType, Ciphersuite, CryptoError, ExporterSecret, HashType, HpkeCiphertext, HpkeConfig,
15        HpkeKeyPair, KemOutput, SignatureScheme,
16    },
17    OpenMlsProvider,
18};
19use tls_codec::SecretVLBytes;
20
21/// A crypto provider that performs all operations via [`RustCrypto`] but only
22/// claims support for an explicit allowlist of ciphersuites.
23pub struct RestrictedCrypto {
24    inner: RustCrypto,
25    allowed: Vec<Ciphersuite>,
26}
27
28impl OpenMlsCrypto for RestrictedCrypto {
29    fn supports(&self, ciphersuite: Ciphersuite) -> Result<(), CryptoError> {
30        if self.allowed.contains(&ciphersuite) {
31            Ok(())
32        } else {
33            Err(CryptoError::UnsupportedCiphersuite)
34        }
35    }
36
37    fn supported_ciphersuites(&self) -> Vec<Ciphersuite> {
38        self.allowed.clone()
39    }
40
41    fn hkdf_extract(
42        &self,
43        hash_type: HashType,
44        salt: &[u8],
45        ikm: &[u8],
46    ) -> Result<SecretVLBytes, CryptoError> {
47        self.inner.hkdf_extract(hash_type, salt, ikm)
48    }
49
50    fn hmac(
51        &self,
52        hash_type: HashType,
53        key: &[u8],
54        message: &[u8],
55    ) -> Result<SecretVLBytes, CryptoError> {
56        self.inner.hmac(hash_type, key, message)
57    }
58
59    fn hkdf_expand(
60        &self,
61        hash_type: HashType,
62        prk: &[u8],
63        info: &[u8],
64        okm_len: usize,
65    ) -> Result<SecretVLBytes, CryptoError> {
66        self.inner.hkdf_expand(hash_type, prk, info, okm_len)
67    }
68
69    fn hash(&self, hash_type: HashType, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
70        self.inner.hash(hash_type, data)
71    }
72
73    fn aead_encrypt(
74        &self,
75        alg: AeadType,
76        key: &[u8],
77        data: &[u8],
78        nonce: &[u8],
79        aad: &[u8],
80    ) -> Result<Vec<u8>, CryptoError> {
81        self.inner.aead_encrypt(alg, key, data, nonce, aad)
82    }
83
84    fn aead_decrypt(
85        &self,
86        alg: AeadType,
87        key: &[u8],
88        ct_tag: &[u8],
89        nonce: &[u8],
90        aad: &[u8],
91    ) -> Result<Vec<u8>, CryptoError> {
92        self.inner.aead_decrypt(alg, key, ct_tag, nonce, aad)
93    }
94
95    fn signature_key_gen(&self, alg: SignatureScheme) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
96        self.inner.signature_key_gen(alg)
97    }
98
99    fn verify_signature(
100        &self,
101        alg: SignatureScheme,
102        data: &[u8],
103        pk: &[u8],
104        signature: &[u8],
105    ) -> Result<(), CryptoError> {
106        self.inner.verify_signature(alg, data, pk, signature)
107    }
108
109    fn sign(&self, alg: SignatureScheme, data: &[u8], key: &[u8]) -> Result<Vec<u8>, CryptoError> {
110        self.inner.sign(alg, data, key)
111    }
112
113    fn hpke_seal(
114        &self,
115        config: HpkeConfig,
116        pk_r: &[u8],
117        info: &[u8],
118        aad: &[u8],
119        ptxt: &[u8],
120    ) -> Result<HpkeCiphertext, CryptoError> {
121        self.inner.hpke_seal(config, pk_r, info, aad, ptxt)
122    }
123
124    fn hpke_open(
125        &self,
126        config: HpkeConfig,
127        input: &HpkeCiphertext,
128        sk_r: &[u8],
129        info: &[u8],
130        aad: &[u8],
131    ) -> Result<Vec<u8>, CryptoError> {
132        self.inner.hpke_open(config, input, sk_r, info, aad)
133    }
134
135    fn hpke_setup_sender_and_export(
136        &self,
137        config: HpkeConfig,
138        pk_r: &[u8],
139        info: &[u8],
140        exporter_context: &[u8],
141        exporter_length: usize,
142    ) -> Result<(KemOutput, ExporterSecret), CryptoError> {
143        self.inner.hpke_setup_sender_and_export(
144            config,
145            pk_r,
146            info,
147            exporter_context,
148            exporter_length,
149        )
150    }
151
152    fn hpke_setup_receiver_and_export(
153        &self,
154        config: HpkeConfig,
155        enc: &[u8],
156        sk_r: &[u8],
157        info: &[u8],
158        exporter_context: &[u8],
159        exporter_length: usize,
160    ) -> Result<ExporterSecret, CryptoError> {
161        self.inner.hpke_setup_receiver_and_export(
162            config,
163            enc,
164            sk_r,
165            info,
166            exporter_context,
167            exporter_length,
168        )
169    }
170
171    fn derive_hpke_keypair(
172        &self,
173        config: HpkeConfig,
174        ikm: &[u8],
175    ) -> Result<HpkeKeyPair, CryptoError> {
176        self.inner.derive_hpke_keypair(config, ikm)
177    }
178
179    #[cfg(feature = "targeted-messages-draft")]
180    fn hpke_open_psk(
181        &self,
182        config: HpkeConfig,
183        input: &HpkeCiphertext,
184        sk_r: &[u8],
185        info: &[u8],
186        aad: &[u8],
187        psk: &[u8],
188        psk_id: &[u8],
189    ) -> Result<Vec<u8>, CryptoError> {
190        self.inner
191            .hpke_open_psk(config, input, sk_r, info, aad, psk, psk_id)
192    }
193
194    #[cfg(feature = "targeted-messages-draft")]
195    fn hpke_seal_psk_resolved_aad<F, E>(
196        &self,
197        config: HpkeConfig,
198        pk_r: &[u8],
199        info: &[u8],
200        ptxt: &[u8],
201        psk: &[u8],
202        psk_id: &[u8],
203        aad_builder: F,
204    ) -> Result<HpkeCiphertext, openmls_traits::crypto::HpkeSealPskResolvedAadError<E>>
205    where
206        Self: Sized,
207        F: FnOnce(&[u8]) -> Result<Vec<u8>, E>,
208    {
209        self.inner
210            .hpke_seal_psk_resolved_aad(config, pk_r, info, ptxt, psk, psk_id, aad_builder)
211    }
212
213    #[cfg(feature = "virtual-clients-draft")]
214    fn ff1_aes128_encrypt(&self, key: &[u8; 16], plaintext: u32) -> Result<u32, CryptoError> {
215        self.inner.ff1_aes128_encrypt(key, plaintext)
216    }
217
218    #[cfg(feature = "virtual-clients-draft")]
219    fn ff1_aes128_decrypt(&self, key: &[u8; 16], ciphertext: u32) -> Result<u32, CryptoError> {
220        self.inner.ff1_aes128_decrypt(key, ciphertext)
221    }
222}
223
224/// An [`OpenMlsProvider`] that only claims support for an explicit allowlist
225/// of ciphersuites while remaining fully functional otherwise.
226pub struct RestrictedProvider {
227    crypto: RestrictedCrypto,
228    inner: OpenMlsRustCrypto,
229}
230
231impl RestrictedProvider {
232    /// Creates a provider that claims support for exactly the given
233    /// ciphersuites.
234    pub fn new(allowed: Vec<Ciphersuite>) -> Self {
235        Self {
236            crypto: RestrictedCrypto {
237                inner: RustCrypto::default(),
238                allowed,
239            },
240            inner: OpenMlsRustCrypto::default(),
241        }
242    }
243
244    /// Returns an unrestricted provider sharing the same storage. Useful to
245    /// set up state (e.g. key packages) that the restricted view then
246    /// processes.
247    pub fn inner(&self) -> &OpenMlsRustCrypto {
248        &self.inner
249    }
250}
251
252impl OpenMlsProvider for RestrictedProvider {
253    type CryptoProvider = RestrictedCrypto;
254    type RandProvider = RustCrypto;
255    type StorageProvider = MemoryStorage;
256
257    fn storage(&self) -> &Self::StorageProvider {
258        self.inner.storage()
259    }
260
261    fn crypto(&self) -> &Self::CryptoProvider {
262        &self.crypto
263    }
264
265    fn rand(&self) -> &Self::RandProvider {
266        self.inner.rand()
267    }
268}