diff --git a/core/Cargo.toml b/core/Cargo.toml index 514ea4b0..2ce49279 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -33,7 +33,6 @@ sha2 = "0.8.0" smallvec = "1.0" unsigned-varint = "0.2" void = "1" -wasm-timer = "0.1" zeroize = "1" [target.'cfg(not(any(target_os = "emscripten", target_os = "unknown")))'.dependencies] @@ -48,7 +47,6 @@ libp2p-secio = { version = "0.13.0", path = "../protocols/secio" } libp2p-swarm = { version = "0.3.0", path = "../swarm" } libp2p-tcp = { version = "0.13.0", path = "../transports/tcp" } quickcheck = "0.9.0" -rand = "0.7.2" wasm-timer = "0.2" [features] diff --git a/protocols/secio/Cargo.toml b/protocols/secio/Cargo.toml index 80808c65..4023a0b9 100644 --- a/protocols/secio/Cargo.toml +++ b/protocols/secio/Cargo.toml @@ -35,7 +35,7 @@ untrusted = "0.7.0" js-sys = "0.3.10" parity-send-wrapper = "0.1" wasm-bindgen = "0.2.33" -wasm-bindgen-futures = "0.3.10" +wasm-bindgen-futures = "0.4.5" web-sys = { version = "0.3.10", features = ["Crypto", "CryptoKey", "SubtleCrypto", "Window"] } [features] diff --git a/protocols/secio/src/exchange/impl_webcrypto.rs b/protocols/secio/src/exchange/impl_webcrypto.rs index 2a883103..a7a363ca 100644 --- a/protocols/secio/src/exchange/impl_webcrypto.rs +++ b/protocols/secio/src/exchange/impl_webcrypto.rs @@ -23,7 +23,7 @@ use crate::{KeyAgreement, SecioError}; use futures::prelude::*; use parity_send_wrapper::SendWrapper; -use std::io; +use std::{io, pin::Pin, task::Context, task::Poll}; use wasm_bindgen::prelude::*; /// Opaque private key type. Contains the private key and the `SubtleCrypto` object. @@ -35,12 +35,11 @@ pub type AgreementPrivateKey = SendSyncHack<(JsValue, web_sys::SubtleCrypto)>; pub struct SendSyncHack(SendWrapper); impl Future for SendSyncHack -where T: Future { - type Item = T::Item; - type Error = T::Error; +where T: Future + Unpin { + type Output = T::Output; - fn poll(&mut self) -> Poll { - self.0.poll() + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + self.0.poll_unpin(cx) } } @@ -48,128 +47,114 @@ where T: Future { /// /// Returns the opaque private key and the corresponding public key. pub fn generate_agreement(algorithm: KeyAgreement) - -> impl Future), Error = SecioError> + -> impl Future), SecioError>> { - // First step is to create the `SubtleCrypto` object. - let crypto = build_crypto_future(); + let future = async move { + // First step is to create the `SubtleCrypto` object. + let crypto = build_crypto_future().await?; - // We then generate the ephemeral key. - let key_promise = crypto.and_then(move |crypto| { - let crypto = crypto.clone(); - let obj = build_curve_obj(algorithm); + // We then generate the ephemeral key. + let key_pair = { + let obj = build_curve_obj(algorithm); - let usages = js_sys::Array::new(); - usages.push(&JsValue::from_str("deriveKey")); - usages.push(&JsValue::from_str("deriveBits")); + let usages = js_sys::Array::new(); + usages.push(&JsValue::from_str("deriveKey")); + usages.push(&JsValue::from_str("deriveBits")); - crypto.generate_key_with_object(&obj, true, usages.as_ref()) - .map(wasm_bindgen_futures::JsFuture::from) - .into_future() - .flatten() - .map(|key_pair| (key_pair, crypto)) - }); + let promise = crypto.generate_key_with_object(&obj, true, usages.as_ref())?; + wasm_bindgen_futures::JsFuture::from(promise).await? + }; - // WebCrypto has generated a key-pair. Let's split this key pair into a private key and a - // public key. - let split_key = key_promise.and_then(move |(key_pair, crypto)| { - let private = js_sys::Reflect::get(&key_pair, &JsValue::from_str("privateKey")); - let public = js_sys::Reflect::get(&key_pair, &JsValue::from_str("publicKey")); - match (private, public) { - (Ok(pr), Ok(pu)) => Ok((pr, pu, crypto)), - (Err(err), _) => Err(err), - (_, Err(err)) => Err(err), - } - }); + // WebCrypto has generated a key-pair. Let's split this key pair into a private key and a + // public key. + let (private, public) = { + let private = js_sys::Reflect::get(&key_pair, &JsValue::from_str("privateKey")); + let public = js_sys::Reflect::get(&key_pair, &JsValue::from_str("publicKey")); + match (private, public) { + (Ok(pr), Ok(pu)) => (pr, pu), + (Err(err), _) => return Err(err), + (_, Err(err)) => return Err(err), + } + }; - // Then we turn the public key into an `ArrayBuffer`. - let export_key = split_key.and_then(move |(private, public, crypto)| { - crypto.export_key("raw", &public.into()) - .map(wasm_bindgen_futures::JsFuture::from) - .into_future() - .flatten() - .map(|public| ((private, crypto), public)) - }); + // Then we turn the public key into an `ArrayBuffer`. + let public = { + let promise = crypto.export_key("raw", &public.into())?; + wasm_bindgen_futures::JsFuture::from(promise).await? + }; - // And finally we convert this `ArrayBuffer` into a `Vec`. - let future = export_key - .map(|((private, crypto), public)| { - let public = js_sys::Uint8Array::new(&public); - let mut public_buf = vec![0; public.length() as usize]; - public.copy_to(&mut public_buf); - (SendSyncHack(SendWrapper::new((private, crypto))), public_buf) + // And finally we convert this `ArrayBuffer` into a `Vec`. + let public = js_sys::Uint8Array::new(&public); + let mut public_buf = vec![0; public.length() as usize]; + public.copy_to(&mut public_buf); + Ok((SendSyncHack(SendWrapper::new((private, crypto))), public_buf)) + }; + + let future = future + .map_err(|err| { + SecioError::IoError(io::Error::new(io::ErrorKind::Other, format!("{:?}", err))) }); - - SendSyncHack(SendWrapper::new(future.map_err(|err| { - SecioError::IoError(io::Error::new(io::ErrorKind::Other, format!("{:?}", err))) - }))) + SendSyncHack(SendWrapper::new(Box::pin(future))) } /// Finish the agreement. On success, returns the shared key that both remote agreed upon. pub fn agree(algorithm: KeyAgreement, key: AgreementPrivateKey, other_public_key: &[u8], out_size: usize) - -> impl Future, Error = SecioError> + -> impl Future, SecioError>> { - let (private_key, crypto) = key.0.take(); - - // We start by importing the remote's public key into the WebCrypto world. - let import_promise = { - let other_public_key = { - // This unsafe is here because the lifetime of `other_public_key` must not outlive the - // `tmp_view`. This is guaranteed by the fact that we clone this array right below. - // See also https://github.com/rustwasm/wasm-bindgen/issues/1303 - let tmp_view = unsafe { js_sys::Uint8Array::view(other_public_key) }; - js_sys::Uint8Array::new(tmp_view.as_ref()) - }; - - // Note: contrary to what one might think, we shouldn't add the "deriveBits" usage. - crypto - .import_key_with_object( - "raw", &js_sys::Object::from(other_public_key.buffer()), - &build_curve_obj(algorithm), false, &js_sys::Array::new() - ) - .into_future() - .map(wasm_bindgen_futures::JsFuture::from) - .flatten() + let other_public_key = { + // This unsafe is here because the lifetime of `other_public_key` must not outlive the + // `tmp_view`. This is guaranteed by the fact that we clone this array right below. + // See also https://github.com/rustwasm/wasm-bindgen/issues/1303 + let tmp_view = unsafe { js_sys::Uint8Array::view(other_public_key) }; + js_sys::Uint8Array::new(tmp_view.as_ref()) }; - // We then derive the final private key. - let derive = import_promise.and_then({ - let crypto = crypto.clone(); - move |public_key| { + let future = async move { + let (private_key, crypto) = key.0.take(); + + // We start by importing the remote's public key into the WebCrypto world. + let public_key = { + // Note: contrary to what one might think, we shouldn't add the "deriveBits" usage. + let promise = crypto + .import_key_with_object( + "raw", &js_sys::Object::from(other_public_key.buffer()), + &build_curve_obj(algorithm), false, &js_sys::Array::new() + )?; + wasm_bindgen_futures::JsFuture::from(promise).await? + }; + + // We then derive the final private key. + let bytes = { let derive_params = build_curve_obj(algorithm); let _ = js_sys::Reflect::set(derive_params.as_ref(), &JsValue::from_str("public"), &public_key); - crypto + let promise = crypto .derive_bits_with_object( &derive_params, &web_sys::CryptoKey::from(private_key), 8 * out_size as u32 - ) - .into_future() - .map(wasm_bindgen_futures::JsFuture::from) - .flatten() - } - }); + )?; + wasm_bindgen_futures::JsFuture::from(promise).await? + }; - let future = derive - .map(|bytes| { - let bytes = js_sys::Uint8Array::new(&bytes); - let mut buf = vec![0; bytes.length() as usize]; - bytes.copy_to(&mut buf); - buf - }) - .map_err(|err| { + let bytes = js_sys::Uint8Array::new(&bytes); + let mut buf = vec![0; bytes.length() as usize]; + bytes.copy_to(&mut buf); + Ok(buf) + }; + + let future = future + .map_err(|err: JsValue| { SecioError::IoError(io::Error::new(io::ErrorKind::Other, format!("{:?}", err))) }); - - SendSyncHack(SendWrapper::new(future)) + SendSyncHack(SendWrapper::new(Box::pin(future))) } /// Builds a future that returns the `SubtleCrypto` object. -fn build_crypto_future() -> impl Future { +async fn build_crypto_future() -> Result { web_sys::window() .ok_or_else(|| JsValue::from_str("Window object not available")) .and_then(|window| window.crypto()) .map(|crypto| crypto.subtle()) - .into_future() } /// Builds a `EcKeyGenParams` object.