Fix the WASM build with stable futures (#1322)

* Fix the WASM build with stable futures

* Fix duplicate dependencies error
This commit is contained in:
Pierre Krieger
2019-11-26 11:48:47 +01:00
committed by GitHub
parent e083e82212
commit e5b087d01f
3 changed files with 83 additions and 100 deletions

View File

@ -33,7 +33,6 @@ sha2 = "0.8.0"
smallvec = "1.0" smallvec = "1.0"
unsigned-varint = "0.2" unsigned-varint = "0.2"
void = "1" void = "1"
wasm-timer = "0.1"
zeroize = "1" zeroize = "1"
[target.'cfg(not(any(target_os = "emscripten", target_os = "unknown")))'.dependencies] [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-swarm = { version = "0.3.0", path = "../swarm" }
libp2p-tcp = { version = "0.13.0", path = "../transports/tcp" } libp2p-tcp = { version = "0.13.0", path = "../transports/tcp" }
quickcheck = "0.9.0" quickcheck = "0.9.0"
rand = "0.7.2"
wasm-timer = "0.2" wasm-timer = "0.2"
[features] [features]

View File

@ -35,7 +35,7 @@ untrusted = "0.7.0"
js-sys = "0.3.10" js-sys = "0.3.10"
parity-send-wrapper = "0.1" parity-send-wrapper = "0.1"
wasm-bindgen = "0.2.33" 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"] } web-sys = { version = "0.3.10", features = ["Crypto", "CryptoKey", "SubtleCrypto", "Window"] }
[features] [features]

View File

@ -23,7 +23,7 @@
use crate::{KeyAgreement, SecioError}; use crate::{KeyAgreement, SecioError};
use futures::prelude::*; use futures::prelude::*;
use parity_send_wrapper::SendWrapper; use parity_send_wrapper::SendWrapper;
use std::io; use std::{io, pin::Pin, task::Context, task::Poll};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
/// Opaque private key type. Contains the private key and the `SubtleCrypto` object. /// 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<T>(SendWrapper<T>); pub struct SendSyncHack<T>(SendWrapper<T>);
impl<T> Future for SendSyncHack<T> impl<T> Future for SendSyncHack<T>
where T: Future { where T: Future + Unpin {
type Item = T::Item; type Output = T::Output;
type Error = T::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
self.0.poll() self.0.poll_unpin(cx)
} }
} }
@ -48,128 +47,114 @@ where T: Future {
/// ///
/// Returns the opaque private key and the corresponding public key. /// Returns the opaque private key and the corresponding public key.
pub fn generate_agreement(algorithm: KeyAgreement) pub fn generate_agreement(algorithm: KeyAgreement)
-> impl Future<Item = (AgreementPrivateKey, Vec<u8>), Error = SecioError> -> impl Future<Output = Result<(AgreementPrivateKey, Vec<u8>), SecioError>>
{ {
// First step is to create the `SubtleCrypto` object. let future = async move {
let crypto = build_crypto_future(); // First step is to create the `SubtleCrypto` object.
let crypto = build_crypto_future().await?;
// We then generate the ephemeral key. // We then generate the ephemeral key.
let key_promise = crypto.and_then(move |crypto| { let key_pair = {
let crypto = crypto.clone(); let obj = build_curve_obj(algorithm);
let obj = build_curve_obj(algorithm);
let usages = js_sys::Array::new(); let usages = js_sys::Array::new();
usages.push(&JsValue::from_str("deriveKey")); usages.push(&JsValue::from_str("deriveKey"));
usages.push(&JsValue::from_str("deriveBits")); usages.push(&JsValue::from_str("deriveBits"));
crypto.generate_key_with_object(&obj, true, usages.as_ref()) let promise = crypto.generate_key_with_object(&obj, true, usages.as_ref())?;
.map(wasm_bindgen_futures::JsFuture::from) wasm_bindgen_futures::JsFuture::from(promise).await?
.into_future() };
.flatten()
.map(|key_pair| (key_pair, crypto))
});
// WebCrypto has generated a key-pair. Let's split this key pair into a private key and a // WebCrypto has generated a key-pair. Let's split this key pair into a private key and a
// public key. // public key.
let split_key = key_promise.and_then(move |(key_pair, crypto)| { let (private, public) = {
let private = js_sys::Reflect::get(&key_pair, &JsValue::from_str("privateKey")); let private = js_sys::Reflect::get(&key_pair, &JsValue::from_str("privateKey"));
let public = js_sys::Reflect::get(&key_pair, &JsValue::from_str("publicKey")); let public = js_sys::Reflect::get(&key_pair, &JsValue::from_str("publicKey"));
match (private, public) { match (private, public) {
(Ok(pr), Ok(pu)) => Ok((pr, pu, crypto)), (Ok(pr), Ok(pu)) => (pr, pu),
(Err(err), _) => Err(err), (Err(err), _) => return Err(err),
(_, Err(err)) => Err(err), (_, Err(err)) => return Err(err),
} }
}); };
// Then we turn the public key into an `ArrayBuffer`. // Then we turn the public key into an `ArrayBuffer`.
let export_key = split_key.and_then(move |(private, public, crypto)| { let public = {
crypto.export_key("raw", &public.into()) let promise = crypto.export_key("raw", &public.into())?;
.map(wasm_bindgen_futures::JsFuture::from) wasm_bindgen_futures::JsFuture::from(promise).await?
.into_future() };
.flatten()
.map(|public| ((private, crypto), public))
});
// And finally we convert this `ArrayBuffer` into a `Vec<u8>`. // And finally we convert this `ArrayBuffer` into a `Vec<u8>`.
let future = export_key let public = js_sys::Uint8Array::new(&public);
.map(|((private, crypto), public)| { let mut public_buf = vec![0; public.length() as usize];
let public = js_sys::Uint8Array::new(&public); public.copy_to(&mut public_buf);
let mut public_buf = vec![0; public.length() as usize]; Ok((SendSyncHack(SendWrapper::new((private, crypto))), public_buf))
public.copy_to(&mut public_buf); };
(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(Box::pin(future)))
SendSyncHack(SendWrapper::new(future.map_err(|err| {
SecioError::IoError(io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))
})))
} }
/// Finish the agreement. On success, returns the shared key that both remote agreed upon. /// 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) pub fn agree(algorithm: KeyAgreement, key: AgreementPrivateKey, other_public_key: &[u8], out_size: usize)
-> impl Future<Item = Vec<u8>, Error = SecioError> -> impl Future<Output = Result<Vec<u8>, SecioError>>
{ {
let (private_key, crypto) = key.0.take(); let other_public_key = {
// This unsafe is here because the lifetime of `other_public_key` must not outlive the
// We start by importing the remote's public key into the WebCrypto world. // `tmp_view`. This is guaranteed by the fact that we clone this array right below.
let import_promise = { // See also https://github.com/rustwasm/wasm-bindgen/issues/1303
let other_public_key = { let tmp_view = unsafe { js_sys::Uint8Array::view(other_public_key) };
// This unsafe is here because the lifetime of `other_public_key` must not outlive the js_sys::Uint8Array::new(tmp_view.as_ref())
// `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()
}; };
// We then derive the final private key. let future = async move {
let derive = import_promise.and_then({ let (private_key, crypto) = key.0.take();
let crypto = crypto.clone();
move |public_key| { // 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 derive_params = build_curve_obj(algorithm);
let _ = js_sys::Reflect::set(derive_params.as_ref(), &JsValue::from_str("public"), &public_key); let _ = js_sys::Reflect::set(derive_params.as_ref(), &JsValue::from_str("public"), &public_key);
crypto let promise = crypto
.derive_bits_with_object( .derive_bits_with_object(
&derive_params, &derive_params,
&web_sys::CryptoKey::from(private_key), &web_sys::CryptoKey::from(private_key),
8 * out_size as u32 8 * out_size as u32
) )?;
.into_future() wasm_bindgen_futures::JsFuture::from(promise).await?
.map(wasm_bindgen_futures::JsFuture::from) };
.flatten()
}
});
let future = derive let bytes = js_sys::Uint8Array::new(&bytes);
.map(|bytes| { let mut buf = vec![0; bytes.length() as usize];
let bytes = js_sys::Uint8Array::new(&bytes); bytes.copy_to(&mut buf);
let mut buf = vec![0; bytes.length() as usize]; Ok(buf)
bytes.copy_to(&mut buf); };
buf
}) let future = future
.map_err(|err| { .map_err(|err: JsValue| {
SecioError::IoError(io::Error::new(io::ErrorKind::Other, format!("{:?}", err))) SecioError::IoError(io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))
}); });
SendSyncHack(SendWrapper::new(Box::pin(future)))
SendSyncHack(SendWrapper::new(future))
} }
/// Builds a future that returns the `SubtleCrypto` object. /// Builds a future that returns the `SubtleCrypto` object.
fn build_crypto_future() -> impl Future<Item = web_sys::SubtleCrypto, Error = JsValue> { async fn build_crypto_future() -> Result<web_sys::SubtleCrypto, JsValue> {
web_sys::window() web_sys::window()
.ok_or_else(|| JsValue::from_str("Window object not available")) .ok_or_else(|| JsValue::from_str("Window object not available"))
.and_then(|window| window.crypto()) .and_then(|window| window.crypto())
.map(|crypto| crypto.subtle()) .map(|crypto| crypto.subtle())
.into_future()
} }
/// Builds a `EcKeyGenParams` object. /// Builds a `EcKeyGenParams` object.