Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
407 changes: 203 additions & 204 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"nucypher-core-python",
"nucypher-core-wasm",
]
resolver = "2"

# Prevents `local exceed maximum` error
# See: https://github.com/rustwasm/wasm-pack/issues/981
Expand Down
2 changes: 1 addition & 1 deletion nucypher-core-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ wasm-bindgen = "0.2.100"
js-sys = "0.3.63"
console_error_panic_hook = { version = "0.1", optional = true }
derive_more = { version = "0.99", default-features = false, features = ["from", "as_ref"] }
wasm-bindgen-derive = "0.2.1"
wasm-bindgen-derive = "0.3"
x25519-dalek = "2.0.0-rc.2"

[dev-dependencies]
Expand Down
84 changes: 16 additions & 68 deletions nucypher-core-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use umbral_pre::bindings_wasm::{
};
use wasm_bindgen::prelude::{wasm_bindgen, JsValue};
use wasm_bindgen::JsCast;
use wasm_bindgen_derive::TryFromJsValue;
use wasm_bindgen_derive::{into_js_array, try_from_js_array, try_from_js_option, TryFromJsValue};

use nucypher_core::ProtocolObject;

Expand Down Expand Up @@ -54,60 +54,6 @@ where
U::from_bytes(data).map(T::from).map_err(map_js_err)
}

/// Tries to convert an optional value (either `null` or a `#[wasm_bindgen]` marked structure)
/// from `JsValue` to the Rust type.
// TODO (rust-umbral#25): This is necessary since wasm-bindgen does not support
// having a parameter of `Option<&T>`, and using `Option<T>` consumes the argument
// (see https://github.com/rustwasm/wasm-bindgen/issues/2370).
fn try_from_js_option<T>(value: impl AsRef<JsValue>) -> Result<Option<T>, Error>
where
for<'a> T: TryFrom<&'a JsValue> + 'static,
for<'a> <T as TryFrom<&'a JsValue>>::Error: core::fmt::Display,
{
let js_value = value.as_ref();

let typed_value = if js_value.is_null() {
None
} else {
Some(T::try_from(js_value).map_err(map_js_err)?)
};
Ok(typed_value)
}

/// Tries to convert a JS array from `JsValue` to a vector of Rust type elements.
// TODO (rust-umbral#23): This is necessary since wasm-bindgen does not support
// having a parameter of `Vec<&T>`
// (see https://github.com/rustwasm/wasm-bindgen/issues/111).
fn try_from_js_array<T>(value: impl AsRef<JsValue>) -> Result<Vec<T>, Error>
where
for<'a> T: TryFrom<&'a JsValue>,
for<'a> <T as TryFrom<&'a JsValue>>::Error: core::fmt::Display,
{
let array: &js_sys::Array = value
.as_ref()
.dyn_ref()
.ok_or_else(|| Error::new("Got a non-array argument where an array was expected"))?;
let length: usize = array.length().try_into().map_err(map_js_err)?;
let mut result = Vec::<T>::with_capacity(length);
for js in array.iter() {
let typed_elem = T::try_from(&js).map_err(map_js_err)?;
result.push(typed_elem);
}
Ok(result)
}

fn into_js_array<T, U>(value: impl IntoIterator<Item = U>) -> T
where
JsValue: From<U>,
T: JsCast,
{
value
.into_iter()
.map(JsValue::from)
.collect::<js_sys::Array>()
.unchecked_into::<T>()
}

macro_rules! generate_from_bytes {
($struct_name:ident) => {
#[wasm_bindgen]
Expand Down Expand Up @@ -305,7 +251,7 @@ impl MessageKit {
plaintext: &[u8],
conditions: &OptionConditions,
) -> Result<MessageKit, Error> {
let typed_conditions = try_from_js_option::<Conditions>(conditions)?;
let typed_conditions = try_from_js_option::<Conditions>(conditions).map_err(map_js_err)?;

Ok(MessageKit(nucypher_core::MessageKit::new(
policy_encrypting_key.as_ref(),
Expand Down Expand Up @@ -335,7 +281,8 @@ impl MessageKit {
policy_encrypting_key: &PublicKey,
vcfrags: &VerifiedCapsuleFragArray,
) -> Result<Box<[u8]>, Error> {
let typed_vcfrags = try_from_js_array::<VerifiedCapsuleFrag>(vcfrags)?;
let typed_vcfrags =
try_from_js_array::<VerifiedCapsuleFrag>(vcfrags).map_err(map_js_err)?;
self.0
.decrypt_reencrypted(
sk.as_ref(),
Expand Down Expand Up @@ -819,7 +766,7 @@ impl ThresholdDecryptionRequest {
acp: &AccessControlPolicy,
context: &OptionContext,
) -> Result<ThresholdDecryptionRequest, Error> {
let typed_context = try_from_js_option::<Context>(context)?;
let typed_context = try_from_js_option::<Context>(context).map_err(map_js_err)?;

Ok(Self(nucypher_core::ThresholdDecryptionRequest::new(
ritual_id,
Expand Down Expand Up @@ -991,9 +938,9 @@ impl ReencryptionRequest {
conditions: &OptionConditions,
context: &OptionContext,
) -> Result<ReencryptionRequest, Error> {
let typed_conditions = try_from_js_option::<Conditions>(conditions)?;
let typed_context = try_from_js_option::<Context>(context)?;
let typed_capsules = try_from_js_array::<Capsule>(capsules)?;
let typed_conditions = try_from_js_option::<Conditions>(conditions).map_err(map_js_err)?;
let typed_context = try_from_js_option::<Context>(context).map_err(map_js_err)?;
let typed_capsules = try_from_js_array::<Capsule>(capsules).map_err(map_js_err)?;
let backend_capules = typed_capsules
.into_iter()
.map(umbral_pre::Capsule::from)
Expand Down Expand Up @@ -1114,7 +1061,7 @@ impl ReencryptionResponse {
policy_encrypting_key: &PublicKey,
bob_encrypting_key: &PublicKey,
) -> Result<VerifiedCapsuleFragArray, Error> {
let typed_capsules = try_from_js_array::<Capsule>(capsules)?;
let typed_capsules = try_from_js_array::<Capsule>(capsules).map_err(map_js_err)?;
let backend_capsules = typed_capsules
.into_iter()
.map(|capsule| capsule.as_ref().clone())
Expand Down Expand Up @@ -1160,8 +1107,9 @@ impl RetrievalKit {
queried_addresses: &AddressArray,
conditions: &OptionConditions,
) -> Result<RetrievalKit, Error> {
let typed_conditions = try_from_js_option::<Conditions>(conditions)?;
let typed_addresses = try_from_js_array::<Address>(queried_addresses)?;
let typed_conditions = try_from_js_option::<Conditions>(conditions).map_err(map_js_err)?;
let typed_addresses =
try_from_js_array::<Address>(queried_addresses).map_err(map_js_err)?;
let backend_addresses = typed_addresses
.into_iter()
.map(|address| address.0)
Expand Down Expand Up @@ -1392,8 +1340,8 @@ impl FleetStateChecksum {
other_nodes: &NodeMetadataArray,
this_node: &OptionNodeMetadata,
) -> Result<FleetStateChecksum, Error> {
let typed_this_node = try_from_js_option::<NodeMetadata>(this_node)?;
let typed_nodes = try_from_js_array::<NodeMetadata>(other_nodes)?;
let typed_this_node = try_from_js_option::<NodeMetadata>(this_node).map_err(map_js_err)?;
let typed_nodes = try_from_js_array::<NodeMetadata>(other_nodes).map_err(map_js_err)?;
let backend_nodes = typed_nodes
.into_iter()
.map(|node| node.0)
Expand Down Expand Up @@ -1427,7 +1375,7 @@ impl MetadataRequest {
fleet_state_checksum: &FleetStateChecksum,
announce_nodes: &NodeMetadataArray,
) -> Result<MetadataRequest, Error> {
let typed_nodes = try_from_js_array::<NodeMetadata>(announce_nodes)?;
let typed_nodes = try_from_js_array::<NodeMetadata>(announce_nodes).map_err(map_js_err)?;
let backend_nodes = typed_nodes
.into_iter()
.map(|node| node.0)
Expand Down Expand Up @@ -1468,7 +1416,7 @@ impl MetadataResponsePayload {
timestamp_epoch: u32,
announce_nodes: &NodeMetadataArray,
) -> Result<MetadataResponsePayload, Error> {
let typed_nodes = try_from_js_array::<NodeMetadata>(announce_nodes)?;
let typed_nodes = try_from_js_array::<NodeMetadata>(announce_nodes).map_err(map_js_err)?;
let backend_nodes = typed_nodes
.into_iter()
.map(|node| node.0)
Expand Down
60 changes: 12 additions & 48 deletions nucypher-core-wasm/tests/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,10 @@ use umbral_pre::bindings_wasm::{
};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_derive::{into_js_array, into_js_option, try_from_js_array};
use wasm_bindgen_test::*;

use nucypher_core_wasm::*;
//
// Test utilities
//

fn into_js_option<T, U>(val: Option<U>) -> T
where
JsValue: From<U>,
T: JsCast,
{
let js_val = match val {
None => JsValue::NULL,
Some(val) => val.into(),
};
js_val.unchecked_into::<T>()
}

fn try_from_js_array<T>(val: impl Into<JsValue>) -> Vec<T>
where
for<'a> T: TryFrom<&'a JsValue>,
for<'a> <T as TryFrom<&'a JsValue>>::Error: core::fmt::Debug,
{
let js_array: js_sys::Array = val.into().dyn_into().unwrap();
js_array
.iter()
.map(|js| T::try_from(&js).unwrap())
.collect::<Vec<_>>()
}

fn into_js_array<T, U>(value: impl IntoIterator<Item = U>) -> T
where
JsValue: From<U>,
T: JsCast,
{
value
.into_iter()
.map(JsValue::from)
.collect::<js_sys::Array>()
.unchecked_into::<T>()
}

/// Generate a random DKG public key.
fn random_dkg_pubkey() -> DkgPublicKey {
Expand Down Expand Up @@ -79,7 +41,7 @@ fn make_kfrags(delegating_sk: &SecretKey, receiving_sk: &SecretKey) -> Vec<Verif
let receiving_pk = receiving_sk.public_key();
let signer = Signer::new(delegating_sk);
let js_kfrags = generate_kfrags(delegating_sk, &receiving_pk, &signer, 2, 3, false, false);
try_from_js_array::<VerifiedKeyFrag>(js_kfrags)
try_from_js_array::<VerifiedKeyFrag>(js_kfrags).unwrap()
}

fn make_fleet_state_checksum() -> FleetStateChecksum {
Expand Down Expand Up @@ -174,7 +136,7 @@ fn message_kit_decrypt_reencrypted() {
);

// Simulate reencryption on the JS side
let vkfrags = try_from_js_array::<VerifiedKeyFrag>(vkfrags_js);
let vkfrags = try_from_js_array::<VerifiedKeyFrag>(vkfrags_js).unwrap();
let vcfrags = vkfrags
.into_iter()
.map(|vkfrag| reencrypt(&message_kit.capsule(), &vkfrag));
Expand Down Expand Up @@ -336,7 +298,7 @@ fn treasure_map_destinations() {
let receiving_sk = SecretKey::random();

let treasure_map = make_treasure_map(&publisher_sk, &receiving_sk);
let destinations_pairs = try_from_js_array::<JsValue>(treasure_map.destinations());
let destinations_pairs = try_from_js_array::<JsValue>(treasure_map.destinations()).unwrap();

// Need to unpack the tuples further
let mut destinations = Vec::new();
Expand Down Expand Up @@ -482,7 +444,7 @@ fn reencryption_response_verify() {
)
.unwrap();

let verified = try_from_js_array::<VerifiedCapsuleFrag>(verified_array);
let verified = try_from_js_array::<VerifiedCapsuleFrag>(verified_array).unwrap();
assert_eq!(vcfrags, verified, "Capsule fragments do not match");

let as_bytes = reencryption_response.to_bytes();
Expand Down Expand Up @@ -512,7 +474,7 @@ fn retrieval_kit() {

let retrieval_kit_from_mk = RetrievalKit::from_message_kit(&message_kit);
let addresses_from_rkit =
try_from_js_array::<Address>(retrieval_kit_from_mk.queried_addresses());
try_from_js_array::<Address>(retrieval_kit_from_mk.queried_addresses()).unwrap();
assert_eq!(
addresses_from_rkit.len(),
0,
Expand All @@ -532,7 +494,8 @@ fn retrieval_kit() {
&conditions_js,
)
.unwrap();
let addresses_from_rkit = try_from_js_array::<Address>(retrieval_kit.queried_addresses());
let addresses_from_rkit =
try_from_js_array::<Address>(retrieval_kit.queried_addresses()).unwrap();
assert_eq!(
addresses_from_rkit.len(),
queried_addresses.len(),
Expand Down Expand Up @@ -620,7 +583,7 @@ fn fleet_state_checksum_to_bytes() {
let fleet_state_checksum = make_fleet_state_checksum();

assert!(
fleet_state_checksum.to_bytes().len() > 0,
!fleet_state_checksum.to_bytes().is_empty(),
"FleetStateChecksum does not serialize to bytes"
);
}
Expand All @@ -636,7 +599,7 @@ fn metadata_request() {
let announce_nodes_js = into_js_array(announce_nodes.iter().cloned());
let metadata_request = MetadataRequest::new(&fleet_state_checksum, &announce_nodes_js).unwrap();

let nodes = try_from_js_array::<NodeMetadata>(metadata_request.announce_nodes());
let nodes = try_from_js_array::<NodeMetadata>(metadata_request.announce_nodes()).unwrap();
assert_eq!(nodes, announce_nodes);

let as_bytes = metadata_request.to_bytes();
Expand All @@ -654,7 +617,8 @@ fn metadata_request() {
#[wasm_bindgen_test]
fn metadata_response_payload() {
let (metadata_response_payload, announce_nodes) = make_metadata_response_payload();
let nodes = try_from_js_array::<NodeMetadata>(metadata_response_payload.announce_nodes());
let nodes =
try_from_js_array::<NodeMetadata>(metadata_response_payload.announce_nodes()).unwrap();
assert_eq!(nodes, announce_nodes, "Announce nodes does not match");
}

Expand Down
7 changes: 6 additions & 1 deletion nucypher-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ umbral-pre = { version = "0.11.0", features = ["serde"] }
ferveo = { package = "ferveo-nucypher", version = "0.4.0" }
ark-std = "0.4"
serde = { version = "1", default-features = false, features = ["derive"] }
generic-array = { version = "0.14", features = ["zeroize"] }
# generic-array added deprecation warnings to its whole API in 0.14.8,
# but many of our RustCrypto dependencies are still stuck on generic-array=0.14.x,
# so it's warnings galore.
generic-array = { version = "=0.14.7", features = ["zeroize"] }
sha3 = "0.10"
rmp-serde = "1"
serde_with = "1.14"
Expand All @@ -27,3 +30,5 @@ zeroize = { version = "1.6.0", features = ["derive"] }
rand_core = "0.6.4"
rand_chacha = "0.3.1"
rand = "0.8.5"
secrecy = "0.10"
serde-encoded-bytes = { version = "0.2", features = ["hex", "base64"] }
4 changes: 2 additions & 2 deletions nucypher-core/src/access_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use alloc::string::String;
use ferveo::api::{encrypt, Ciphertext, DkgPublicKey, SecretBox};
use ferveo::Error;
use serde::{Deserialize, Serialize};
use umbral_pre::serde_bytes;
use serde_encoded_bytes::{Base64, SliceLike};

use crate::conditions::Conditions;
use crate::versioning::{
Expand Down Expand Up @@ -89,7 +89,7 @@ pub struct AccessControlPolicy {
pub auth_data: AuthenticatedData,

/// The authorization data for the authenticated data
#[serde(with = "serde_bytes::as_base64")]
#[serde(with = "SliceLike::<Base64>")]
pub authorization: Box<[u8]>,
}

Expand Down
5 changes: 3 additions & 2 deletions nucypher-core/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use generic_array::{
GenericArray,
};
use serde::{Deserialize, Serialize};
use serde_encoded_bytes::{ArrayLike, Hex};
use sha3::{digest::Update, Digest, Keccak256};
use umbral_pre::{serde_bytes, PublicKey};
use umbral_pre::PublicKey;

// We could use the third-party `ethereum_types::Address` here,
// but it has an inefficient `serde` implementation (serializes as hex instead of bytes).
Expand All @@ -14,7 +15,7 @@ use umbral_pre::{serde_bytes, PublicKey};

/// Represents an Ethereum address (20 bytes).
#[derive(PartialEq, Debug, Serialize, Deserialize, Copy, Clone, PartialOrd, Eq, Ord)]
pub struct Address(#[serde(with = "serde_bytes::as_hex")] [u8; Address::SIZE]);
pub struct Address(#[serde(with = "ArrayLike::<Hex>")] [u8; Address::SIZE]);

impl Address {
/// Size of canonical Ethereum address, in bytes.
Expand Down
Loading
Loading