mls_rs_crypto_webcrypto/
lib.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright by contributors to this project.
3// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4
5#![cfg(all(mls_build_async, target_arch = "wasm32"))]
6
7mod aead;
8mod ec;
9mod hkdf;
10mod key_type;
11
12use mls_rs_core::{
13    crypto::{
14        CipherSuite, CipherSuiteProvider, CryptoProvider, HpkeCiphertext, HpkePublicKey,
15        HpkeSecretKey, SignaturePublicKey, SignatureSecretKey,
16    },
17    error::{AnyError, IntoAnyError},
18};
19
20use mls_rs_crypto_hpke::{
21    context::{ContextR, ContextS},
22    dhkem::DhKem,
23    hpke::Hpke,
24};
25
26use mls_rs_crypto_traits::{AeadType, KdfType, KemId};
27
28use wasm_bindgen::JsValue;
29use web_sys::SubtleCrypto;
30use zeroize::Zeroizing;
31
32use crate::{
33    aead::Aead,
34    ec::{EcSigner, Ecdh},
35    hkdf::Hkdf,
36};
37
38#[derive(Debug, thiserror::Error)]
39pub enum CryptoError {
40    #[error("Unsupported ciphersuite")]
41    UnsupportedCipherSuite,
42    #[error("JS error {0}")]
43    JsValue(String),
44    #[error("Key has wrong length for cipher suite")]
45    WrongKeyLength,
46    #[error("Window not found")]
47    WindowNotFound,
48    #[error("Invalid signature")]
49    InvalidSignature,
50    #[error("Der encoding error {0}")]
51    DerError(String),
52    #[error("Could not compute EC public key from seed")]
53    CouldNotComputePublicKey,
54    #[error(transparent)]
55    HpkeError(AnyError),
56}
57
58impl From<JsValue> for CryptoError {
59    fn from(e: JsValue) -> Self {
60        Self::JsValue(format!("{e:?}"))
61    }
62}
63
64#[inline]
65pub(crate) fn get_crypto() -> Result<SubtleCrypto, CryptoError> {
66    Ok(web_sys::window()
67        .ok_or(CryptoError::WindowNotFound)?
68        .crypto()?
69        .subtle())
70}
71
72#[derive(Clone, Default, Debug)]
73pub struct WebCryptoProvider;
74
75impl WebCryptoProvider {
76    pub fn new() -> Self {
77        Self
78    }
79
80    pub fn all_supported_cipher_suites() -> Vec<CipherSuite> {
81        vec![
82            CipherSuite::P256_AES128,
83            CipherSuite::P384_AES256,
84            CipherSuite::P521_AES256,
85        ]
86    }
87}
88
89#[derive(Clone)]
90pub struct WebCryptoCipherSuite {
91    aead: Aead,
92    hkdf: Hkdf,
93    ec_signer: EcSigner,
94    hpke: Hpke<DhKem<Ecdh, Hkdf>, Hkdf, Aead>,
95    cipher_suite: CipherSuite,
96}
97
98impl WebCryptoCipherSuite {
99    pub fn new(cipher_suite: CipherSuite) -> Option<Self> {
100        let kem_id = KemId::new(cipher_suite)?;
101        let hkdf = Hkdf::new(cipher_suite)?;
102        let dh = Ecdh::new(cipher_suite)?;
103
104        let dhkem = DhKem::new(dh, hkdf.clone(), kem_id as u16, kem_id.n_secret());
105        let aead = Aead::new(cipher_suite)?;
106
107        Some(Self {
108            aead: aead.clone(),
109            hkdf: hkdf.clone(),
110            ec_signer: EcSigner::new(cipher_suite)?,
111            hpke: Hpke::new(dhkem, hkdf, Some(aead)),
112            cipher_suite,
113        })
114    }
115}
116
117impl CryptoProvider for WebCryptoProvider {
118    type CipherSuiteProvider = WebCryptoCipherSuite;
119
120    fn supported_cipher_suites(&self) -> Vec<CipherSuite> {
121        Self::all_supported_cipher_suites()
122    }
123
124    fn cipher_suite_provider(&self, cipher_suite: CipherSuite) -> Option<WebCryptoCipherSuite> {
125        WebCryptoCipherSuite::new(cipher_suite)
126    }
127}
128
129impl IntoAnyError for CryptoError {}
130
131#[maybe_async::must_be_async(?Send)]
132impl CipherSuiteProvider for WebCryptoCipherSuite {
133    type Error = CryptoError;
134
135    type HpkeContextS = ContextS<Hkdf, Aead>;
136    type HpkeContextR = ContextR<Hkdf, Aead>;
137
138    fn cipher_suite(&self) -> mls_rs_core::crypto::CipherSuite {
139        self.cipher_suite
140    }
141
142    async fn hash(&self, data: &[u8]) -> Result<Vec<u8>, Self::Error> {
143        self.hkdf.hash(data).await
144    }
145
146    async fn mac(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>, Self::Error> {
147        self.hkdf.hmac(key, data).await
148    }
149
150    async fn aead_seal(
151        &self,
152        key: &[u8],
153        data: &[u8],
154        aad: Option<&[u8]>,
155        nonce: &[u8],
156    ) -> Result<Vec<u8>, Self::Error> {
157        self.aead.seal(key, data, aad, nonce).await
158    }
159
160    async fn aead_open(
161        &self,
162        key: &[u8],
163        ciphertext: &[u8],
164        aad: Option<&[u8]>,
165        nonce: &[u8],
166    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
167        self.aead
168            .open(key, ciphertext, aad, nonce)
169            .await
170            .map(Zeroizing::new)
171    }
172
173    fn aead_key_size(&self) -> usize {
174        self.aead.key_size()
175    }
176
177    fn aead_nonce_size(&self) -> usize {
178        self.aead.nonce_size()
179    }
180
181    async fn kdf_extract(
182        &self,
183        salt: &[u8],
184        ikm: &[u8],
185    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
186        self.hkdf.extract(salt, ikm).await.map(Zeroizing::new)
187    }
188
189    async fn kdf_expand(
190        &self,
191        prk: &[u8],
192        info: &[u8],
193        len: usize,
194    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
195        self.hkdf.expand(prk, info, len).await.map(Zeroizing::new)
196    }
197
198    fn kdf_extract_size(&self) -> usize {
199        self.hkdf.extract_size()
200    }
201
202    async fn hpke_seal(
203        &self,
204        remote_key: &HpkePublicKey,
205        info: &[u8],
206        aad: Option<&[u8]>,
207        pt: &[u8],
208    ) -> Result<HpkeCiphertext, Self::Error> {
209        self.hpke
210            .seal(remote_key, info, None, aad, pt)
211            .await
212            .map_err(|e| CryptoError::HpkeError(e.into_any_error()))
213    }
214
215    async fn hpke_open(
216        &self,
217        ciphertext: &HpkeCiphertext,
218        local_secret: &HpkeSecretKey,
219        local_public: &HpkePublicKey,
220        info: &[u8],
221        aad: Option<&[u8]>,
222    ) -> Result<Vec<u8>, Self::Error> {
223        self.hpke
224            .open(ciphertext, local_secret, local_public, info, None, aad)
225            .await
226            .map_err(|e| CryptoError::HpkeError(e.into_any_error()))
227    }
228
229    async fn hpke_setup_s(
230        &self,
231        remote_key: &HpkePublicKey,
232        info: &[u8],
233    ) -> Result<(Vec<u8>, Self::HpkeContextS), Self::Error> {
234        self.hpke
235            .setup_sender(remote_key, info, None)
236            .await
237            .map_err(|e| CryptoError::HpkeError(e.into_any_error()))
238    }
239
240    async fn hpke_setup_r(
241        &self,
242        kem_output: &[u8],
243        local_secret: &HpkeSecretKey,
244        local_public: &HpkePublicKey,
245        info: &[u8],
246    ) -> Result<Self::HpkeContextR, Self::Error> {
247        self.hpke
248            .setup_receiver(kem_output, local_secret, local_public, info, None)
249            .await
250            .map_err(|e| CryptoError::HpkeError(e.into_any_error()))
251    }
252
253    async fn kem_derive(&self, ikm: &[u8]) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
254        self.hpke
255            .derive(ikm)
256            .await
257            .map_err(|e| CryptoError::HpkeError(e.into_any_error()))
258    }
259
260    async fn kem_generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
261        self.hpke
262            .generate()
263            .await
264            .map_err(|e| CryptoError::HpkeError(e.into_any_error()))
265    }
266
267    fn kem_public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error> {
268        self.hpke
269            .public_key_validate(key)
270            .map_err(|e| CryptoError::HpkeError(e.into_any_error()))
271    }
272
273    fn random_bytes(&self, out: &mut [u8]) -> Result<(), Self::Error> {
274        web_sys::window()
275            .ok_or(CryptoError::WindowNotFound)?
276            .crypto()?
277            .get_random_values_with_u8_array(out)?;
278
279        Ok(())
280    }
281
282    async fn signature_key_generate(
283        &self,
284    ) -> Result<(SignatureSecretKey, SignaturePublicKey), Self::Error> {
285        self.ec_signer.generate().await
286    }
287
288    async fn signature_key_derive_public(
289        &self,
290        secret_key: &SignatureSecretKey,
291    ) -> Result<SignaturePublicKey, Self::Error> {
292        self.ec_signer.derive_public(secret_key)
293    }
294
295    async fn sign(
296        &self,
297        secret_key: &SignatureSecretKey,
298        data: &[u8],
299    ) -> Result<Vec<u8>, Self::Error> {
300        self.ec_signer.sign(secret_key, data).await
301    }
302
303    async fn verify(
304        &self,
305        public_key: &SignaturePublicKey,
306        signature: &[u8],
307        data: &[u8],
308    ) -> Result<(), Self::Error> {
309        self.ec_signer.verify(public_key, data, signature).await
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
316
317    #[wasm_bindgen_test::wasm_bindgen_test]
318    async fn mls_rs_core_test() {
319        mls_rs_core::crypto::test_suite::verify_tests(&crate::WebCryptoProvider, false).await
320    }
321}