diff --git a/mls-rs-core/Cargo.toml b/mls-rs-core/Cargo.toml index 431900e0..d15ba52a 100644 --- a/mls-rs-core/Cargo.toml +++ b/mls-rs-core/Cargo.toml @@ -11,6 +11,7 @@ exclude = ["test_data"] [features] +node = [] default = ["std", "rfc_compliant", "fast_serialize"] arbitrary = ["std", "dep:arbitrary"] fast_serialize = ["mls-rs-codec/preallocate"] diff --git a/mls-rs-core/js/es-time.js b/mls-rs-core/js/es-time.js new file mode 100644 index 00000000..cb8540bc --- /dev/null +++ b/mls-rs-core/js/es-time.js @@ -0,0 +1,3 @@ +export function date_now() { + return Date.now(); +} diff --git a/mls-rs-core/js/node-time.js b/mls-rs-core/js/node-time.js new file mode 100644 index 00000000..a3c82b1c --- /dev/null +++ b/mls-rs-core/js/node-time.js @@ -0,0 +1,4 @@ + +module.exports.date_now = function() { + return Date.now(); +}; diff --git a/mls-rs-core/src/time.rs b/mls-rs-core/src/time.rs index 57f7a28c..d5079b3e 100644 --- a/mls-rs-core/src/time.rs +++ b/mls-rs-core/src/time.rs @@ -48,11 +48,14 @@ impl From for MlsTime { } } -#[cfg(target_arch = "wasm32")] -#[wasm_bindgen(inline_js = r#" -export function date_now() { - return Date.now(); -}"#)] +#[cfg_attr( + all(target_arch = "wasm32", not(feature = "node")), + wasm_bindgen(module = "/js/es-time.js") +)] +#[cfg_attr( + all(target_arch = "wasm32", feature = "node"), + wasm_bindgen(module = "/js/node-time.js") +)] extern "C" { fn date_now() -> f64; } diff --git a/mls-rs-crypto-webcrypto/Cargo.toml b/mls-rs-crypto-webcrypto/Cargo.toml index f5ba1757..53a4f223 100644 --- a/mls-rs-crypto-webcrypto/Cargo.toml +++ b/mls-rs-crypto-webcrypto/Cargo.toml @@ -8,7 +8,11 @@ repository = "https://github.com/awslabs/mls-rs" keywords = ["mls", "mls-rs"] license = "Apache-2.0 OR MIT" +[features] +node = [] + [dependencies] +cfg-if = "1.0.0" mls-rs-core = { path = "../mls-rs-core", default-features = false, features = ["std"], version = "0.19.0" } mls-rs-crypto-hpke = { path = "../mls-rs-crypto-hpke", default-features = false, features = ["std"], version = "0.10.0" } mls-rs-crypto-traits = { path = "../mls-rs-crypto-traits", default-features = false, features = ["std"], version = "0.11.0" } diff --git a/mls-rs-crypto-webcrypto/js/node-crypto.js b/mls-rs-crypto-webcrypto/js/node-crypto.js new file mode 100644 index 00000000..db2e4e54 --- /dev/null +++ b/mls-rs-crypto-webcrypto/js/node-crypto.js @@ -0,0 +1,5 @@ +const crypto = require("node:crypto").webcrypto; + +module.exports.node_crypto = function() { + return crypto; +}; \ No newline at end of file diff --git a/mls-rs-crypto-webcrypto/src/key_type.rs b/mls-rs-crypto-webcrypto/src/key_type.rs index 381cf1bc..d964844e 100644 --- a/mls-rs-crypto-webcrypto/src/key_type.rs +++ b/mls-rs-crypto-webcrypto/src/key_type.rs @@ -2,7 +2,7 @@ // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use js_sys::{Array, Reflect, Uint8Array}; +use js_sys::{Array, Reflect, Uint8Array, Promise}; use wasm_bindgen::JsValue; use wasm_bindgen_futures::JsFuture; use web_sys::{CryptoKey, CryptoKeyPair, EcKeyImportParams, HmacImportParams, SubtleCrypto}; @@ -54,6 +54,22 @@ impl KeyType { } } + #[cfg(not(feature = "node"))] + async fn import_with_public_info(&self, crypto: &SubtleCrypto, key: &Uint8Array, params: &EcKeyImportParams, key_usages: &Array) -> Result { + crypto.import_key_with_object(self.format(), &key, ¶ms, true, &key_usages) + } + + #[cfg(feature = "node")] + async fn import_with_public_info(&self, crypto: &SubtleCrypto, bytes: &Uint8Array, params: &EcKeyImportParams, key_usages: &Array) -> Result { + let crypto_key_promise = crypto.import_key_with_object(self.format(), bytes, params, true, key_usages)?; + let crypto_key = JsFuture::from(crypto_key_promise).await?.into(); + // Export the key to jwk to force the generation of the public key. + let jwk_promise = crypto.export_key(&"jwk", &crypto_key)?; + let jwk = JsFuture::from(jwk_promise).await?; + // Re-import into the original requested format with the same usages from jwk. + crypto.import_key_with_object("jwk", &jwk.into(), params, true, &key_usages) + } + pub(crate) async fn import( &self, crypto: &SubtleCrypto, @@ -87,9 +103,8 @@ impl KeyType { | KeyType::EcdsaPublic(curve) | KeyType::EcdsaSecret(curve) => { let mut params = EcKeyImportParams::new(self.algorithm()); - params.named_curve(curve); - - crypto.import_key_with_object(self.format(), &key, ¶ms, true, &key_usages)? + params.set_named_curve(curve); + self.import_with_public_info(&crypto, &key, ¶ms, &key_usages).await? } }; diff --git a/mls-rs-crypto-webcrypto/src/lib.rs b/mls-rs-crypto-webcrypto/src/lib.rs index 259dc4bc..ddf1fdff 100644 --- a/mls-rs-crypto-webcrypto/src/lib.rs +++ b/mls-rs-crypto-webcrypto/src/lib.rs @@ -11,7 +11,7 @@ mod key_type; use mls_rs_core::{ crypto::{ - CipherSuite, CipherSuiteProvider, CryptoProvider, HpkeCiphertext, HpkePublicKey, + self, CipherSuite, CipherSuiteProvider, CryptoProvider, HpkeCiphertext, HpkePublicKey, HpkeSecretKey, SignaturePublicKey, SignatureSecretKey, }, error::{AnyError, IntoAnyError}, @@ -25,6 +25,8 @@ use mls_rs_crypto_hpke::{ use mls_rs_crypto_traits::{AeadType, KdfType, KemId}; +use cfg_if::cfg_if; +use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; use web_sys::SubtleCrypto; use zeroize::Zeroizing; @@ -34,6 +36,7 @@ use crate::{ ec::{EcSigner, Ecdh}, hkdf::Hkdf, }; +use web_sys::Crypto; #[derive(Debug, thiserror::Error)] pub enum CryptoError { @@ -61,12 +64,37 @@ impl From for CryptoError { } } -#[inline] -pub(crate) fn get_crypto() -> Result { - Ok(web_sys::window() - .ok_or(CryptoError::WindowNotFound)? - .crypto()? - .subtle()) +cfg_if! { + if #[cfg(feature = "node")] { + #[wasm_bindgen(module = "/js/node-crypto.js")] + extern "C" { + #[wasm_bindgen] + fn node_crypto() -> Crypto; + } + + #[inline] + pub(crate) fn crypto() -> Result { + Ok(node_crypto()) + } + + #[inline] + pub(crate) fn get_crypto() -> Result { + Ok(node_crypto() + .subtle()) + } + } else { + #[inline] + pub(crate) fn crypto() -> Result { + Ok(web_sys::window() + .ok_or(CryptoError::WindowNotFound)? + .crypto()?) + } + + #[inline] + pub(crate) fn get_crypto() -> Result { + crypto().map(|c| c.subtle()) + } + } } #[derive(Clone, Default, Debug)] @@ -271,10 +299,7 @@ impl CipherSuiteProvider for WebCryptoCipherSuite { } fn random_bytes(&self, out: &mut [u8]) -> Result<(), Self::Error> { - web_sys::window() - .ok_or(CryptoError::WindowNotFound)? - .crypto()? - .get_random_values_with_u8_array(out)?; + crypto()?.get_random_values_with_u8_array(out)?; Ok(()) }