From ca11f6075d10c45edf610d24662cffc08bd57a53 Mon Sep 17 00:00:00 2001 From: Brandon Vandegrift <798832-bmv437@users.noreply.gitlab.com> Date: Sun, 3 Sep 2023 23:25:04 -0400 Subject: [PATCH] (wasm) Add VeilidCrypto class, refine interfaces for VeilidRoutingContext --- .../src/crypto/types/byte_array_types.rs | 1 + veilid-core/src/crypto/types/crypto_typed.rs | 2 +- veilid-core/src/crypto/types/keypair.rs | 8 + .../src/veilid_api/types/aligned_u64.rs | 1 + .../src/veilid_api/types/app_message_call.rs | 3 +- .../types/dht/dht_record_descriptor.rs | 9 +- .../src/veilid_api/types/dht/schema/dflt.rs | 1 + .../src/veilid_api/types/dht/schema/smpl.rs | 3 + .../src/veilid_api/types/dht/value_data.rs | 4 +- veilid-core/src/veilid_api/types/safety.rs | 2 +- veilid-core/src/veilid_api/types/stats.rs | 5 + .../src/veilid_api/types/veilid_state.rs | 1 + veilid-wasm/src/lib.rs | 11 +- veilid-wasm/src/veilid_crypto_js.rs | 482 ++++++++++++++++++ veilid-wasm/src/veilid_routing_context_js.rs | 156 +++--- veilid-wasm/src/wasm_helpers.rs | 21 + 16 files changed, 623 insertions(+), 87 deletions(-) create mode 100644 veilid-wasm/src/veilid_crypto_js.rs create mode 100644 veilid-wasm/src/wasm_helpers.rs diff --git a/veilid-core/src/crypto/types/byte_array_types.rs b/veilid-core/src/crypto/types/byte_array_types.rs index d3bdee80..d47a323a 100644 --- a/veilid-core/src/crypto/types/byte_array_types.rs +++ b/veilid-core/src/crypto/types/byte_array_types.rs @@ -78,6 +78,7 @@ where macro_rules! byte_array_type { ($name:ident, $size:expr, $encoded_size:expr) => { #[derive(Clone, Copy, Hash)] + #[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))] pub struct $name { pub bytes: [u8; $size], } diff --git a/veilid-core/src/crypto/types/crypto_typed.rs b/veilid-core/src/crypto/types/crypto_typed.rs index 4717b72a..39f4bd59 100644 --- a/veilid-core/src/crypto/types/crypto_typed.rs +++ b/veilid-core/src/crypto/types/crypto_typed.rs @@ -1,7 +1,7 @@ use super::*; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] +#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))] pub struct CryptoTyped where K: Clone diff --git a/veilid-core/src/crypto/types/keypair.rs b/veilid-core/src/crypto/types/keypair.rs index f8e6e6da..333a5d0b 100644 --- a/veilid-core/src/crypto/types/keypair.rs +++ b/veilid-core/src/crypto/types/keypair.rs @@ -1,10 +1,18 @@ use super::*; #[derive(Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[cfg_attr( + target_arch = "wasm32", + derive(Tsify), + tsify(from_wasm_abi, into_wasm_abi) +)] pub struct KeyPair { + #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] pub key: PublicKey, + #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] pub secret: SecretKey, } +from_impl_to_jsvalue!(KeyPair); impl KeyPair { pub fn new(key: PublicKey, secret: SecretKey) -> Self { diff --git a/veilid-core/src/veilid_api/types/aligned_u64.rs b/veilid-core/src/veilid_api/types/aligned_u64.rs index 1e2e9332..510cdba3 100644 --- a/veilid-core/src/veilid_api/types/aligned_u64.rs +++ b/veilid-core/src/veilid_api/types/aligned_u64.rs @@ -14,6 +14,7 @@ use super::*; pub struct AlignedU64( #[serde(with = "as_human_string")] #[schemars(with = "String")] + #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] u64, ); diff --git a/veilid-core/src/veilid_api/types/app_message_call.rs b/veilid-core/src/veilid_api/types/app_message_call.rs index 01ec3d76..5af5b692 100644 --- a/veilid-core/src/veilid_api/types/app_message_call.rs +++ b/veilid-core/src/veilid_api/types/app_message_call.rs @@ -6,11 +6,12 @@ use super::*; pub struct VeilidAppMessage { #[serde(with = "as_human_opt_string")] #[schemars(with = "Option")] - #[cfg_attr(target_arch = "wasm32", tsify(optional))] + #[cfg_attr(target_arch = "wasm32", tsify(optional, type = "string"))] sender: Option, #[serde(with = "as_human_base64")] #[schemars(with = "String")] + #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] message: Vec, } diff --git a/veilid-core/src/veilid_api/types/dht/dht_record_descriptor.rs b/veilid-core/src/veilid_api/types/dht/dht_record_descriptor.rs index 43881601..32c93ffd 100644 --- a/veilid-core/src/veilid_api/types/dht/dht_record_descriptor.rs +++ b/veilid-core/src/veilid_api/types/dht/dht_record_descriptor.rs @@ -2,7 +2,11 @@ use super::*; /// DHT Record Descriptor #[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] +#[cfg_attr( + target_arch = "wasm32", + derive(Tsify), + tsify(from_wasm_abi, into_wasm_abi) +)] pub struct DHTRecordDescriptor { /// DHT Key = Hash(ownerKeyKind) of: [ ownerKeyValue, schema ] #[schemars(with = "String")] @@ -15,11 +19,12 @@ pub struct DHTRecordDescriptor { /// If this key is being created: Some(the secret key of the owner) /// If this key is just being opened: None #[schemars(with = "Option")] - #[cfg_attr(target_arch = "wasm32", tsify(type = "string | undefined"))] + #[cfg_attr(target_arch = "wasm32", tsify(optional, type = "string"))] owner_secret: Option, /// The schema in use associated with the key schema: DHTSchema, } +from_impl_to_jsvalue!(DHTRecordDescriptor); impl DHTRecordDescriptor { pub fn new( diff --git a/veilid-core/src/veilid_api/types/dht/schema/dflt.rs b/veilid-core/src/veilid_api/types/dht/schema/dflt.rs index b2c6b2b7..bb4e7c6e 100644 --- a/veilid-core/src/veilid_api/types/dht/schema/dflt.rs +++ b/veilid-core/src/veilid_api/types/dht/schema/dflt.rs @@ -2,6 +2,7 @@ use super::*; /// Default DHT Schema (DFLT) #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi))] pub struct DHTSchemaDFLT { /// Owner subkey count pub o_cnt: u16, diff --git a/veilid-core/src/veilid_api/types/dht/schema/smpl.rs b/veilid-core/src/veilid_api/types/dht/schema/smpl.rs index 572b93e8..d6da5ea6 100644 --- a/veilid-core/src/veilid_api/types/dht/schema/smpl.rs +++ b/veilid-core/src/veilid_api/types/dht/schema/smpl.rs @@ -2,9 +2,11 @@ use super::*; /// Simple DHT Schema (SMPL) Member #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi))] pub struct DHTSchemaSMPLMember { /// Member key #[schemars(with = "String")] + #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] pub m_key: PublicKey, /// Member subkey count pub m_cnt: u16, @@ -12,6 +14,7 @@ pub struct DHTSchemaSMPLMember { /// Simple DHT Schema (SMPL) #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(from_wasm_abi))] pub struct DHTSchemaSMPL { /// Owner subkey count pub o_cnt: u16, diff --git a/veilid-core/src/veilid_api/types/dht/value_data.rs b/veilid-core/src/veilid_api/types/dht/value_data.rs index c7cbdb42..34d6d8ac 100644 --- a/veilid-core/src/veilid_api/types/dht/value_data.rs +++ b/veilid-core/src/veilid_api/types/dht/value_data.rs @@ -2,7 +2,7 @@ use super::*; use veilid_api::VeilidAPIResult; #[derive(Clone, Default, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] +#[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))] pub struct ValueData { /// An increasing sequence number to time-order the DHT record changes seq: ValueSeqNum, @@ -18,6 +18,8 @@ pub struct ValueData { #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] writer: PublicKey, } +from_impl_to_jsvalue!(ValueData); + impl ValueData { pub const MAX_LEN: usize = 32768; diff --git a/veilid-core/src/veilid_api/types/safety.rs b/veilid-core/src/veilid_api/types/safety.rs index 749efce9..18c7b247 100644 --- a/veilid-core/src/veilid_api/types/safety.rs +++ b/veilid-core/src/veilid_api/types/safety.rs @@ -68,7 +68,7 @@ impl Default for SafetySelection { pub struct SafetySpec { /// preferred safety route set id if it still exists #[schemars(with = "Option")] - #[cfg_attr(target_arch = "wasm32", tsify(type = "string | undefined"))] + #[cfg_attr(target_arch = "wasm32", tsify(optional, type = "string"))] pub preferred_route: Option, /// must be greater than 0 pub hop_count: usize, diff --git a/veilid-core/src/veilid_api/types/stats.rs b/veilid-core/src/veilid_api/types/stats.rs index 836cb728..5a6b27a8 100644 --- a/veilid-core/src/veilid_api/types/stats.rs +++ b/veilid-core/src/veilid_api/types/stats.rs @@ -1,6 +1,7 @@ use super::*; #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(target_arch = "wasm32", derive(Tsify))] pub struct LatencyStats { pub fastest: TimestampDuration, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies pub average: TimestampDuration, // average latency over the ROLLING_LATENCIES_SIZE last latencies @@ -8,6 +9,7 @@ pub struct LatencyStats { } #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(target_arch = "wasm32", derive(Tsify))] pub struct TransferStats { pub total: ByteCount, // total amount transferred ever pub maximum: ByteCount, // maximum rate over the ROLLING_TRANSFERS_SIZE last amounts @@ -16,12 +18,14 @@ pub struct TransferStats { } #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(target_arch = "wasm32", derive(Tsify))] pub struct TransferStatsDownUp { pub down: TransferStats, pub up: TransferStats, } #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(target_arch = "wasm32", derive(Tsify))] pub struct RPCStats { pub messages_sent: u32, // number of rpcs that have been sent in the total_time range pub messages_rcvd: u32, // number of rpcs that have been received in the total_time range @@ -34,6 +38,7 @@ pub struct RPCStats { } #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[cfg_attr(target_arch = "wasm32", derive(Tsify))] pub struct PeerStats { pub time_added: Timestamp, // when the peer was added to the routing table pub rpc_stats: RPCStats, // information about RPCs diff --git a/veilid-core/src/veilid_api/types/veilid_state.rs b/veilid-core/src/veilid_api/types/veilid_state.rs index 8443572b..d6f5f836 100644 --- a/veilid-core/src/veilid_api/types/veilid_state.rs +++ b/veilid-core/src/veilid_api/types/veilid_state.rs @@ -97,6 +97,7 @@ pub struct VeilidStateConfig { #[cfg_attr(target_arch = "wasm32", derive(Tsify))] pub struct VeilidValueChange { #[schemars(with = "String")] + #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] pub key: TypedKey, pub subkeys: Vec, pub count: u32, diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index 5203d431..29647d6b 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -26,9 +26,13 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_futures::*; pub mod veilid_client_js; +pub mod veilid_crypto_js; pub mod veilid_routing_context_js; pub mod veilid_table_db_js; +mod wasm_helpers; +use wasm_helpers::*; + // Allocator extern crate wee_alloc; #[global_allocator] @@ -146,13 +150,18 @@ pub struct VeilidWASMConfig { } #[derive(Debug, Deserialize, Serialize)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] +#[cfg_attr( + target_arch = "wasm32", + derive(Tsify), + tsify(from_wasm_abi, into_wasm_abi) +)] pub struct VeilidRouteBlob { pub route_id: veilid_core::RouteId, #[serde(with = "veilid_core::as_human_base64")] #[cfg_attr(target_arch = "wasm32", tsify(type = "string"))] pub blob: Vec, } +from_impl_to_jsvalue!(VeilidRouteBlob); // WASM Bindings diff --git a/veilid-wasm/src/veilid_crypto_js.rs b/veilid-wasm/src/veilid_crypto_js.rs new file mode 100644 index 00000000..ecef57ca --- /dev/null +++ b/veilid-wasm/src/veilid_crypto_js.rs @@ -0,0 +1,482 @@ +#![allow(non_snake_case)] +use super::*; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "string[]")] + pub type ValidCryptoKinds; +} + +#[wasm_bindgen(js_class = veilidCrypto)] +pub struct VeilidCrypto {} + +#[wasm_bindgen(js_class = veilidCrypto)] +impl VeilidCrypto { + pub fn validCryptoKinds() -> ValidCryptoKinds { + let res = veilid_core::VALID_CRYPTO_KINDS + .iter() + .map(|k| (*k).to_string()); + res.map(JsValue::from) + .collect::() + .unchecked_into::() + } + + pub fn bestCryptoKind() -> String { + veilid_core::best_crypto_kind().to_string() + } + + pub fn cachedDh(kind: String, key: String, secret: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; + let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_cached_dh", + "kind", + kind.to_string(), + ) + })?; + let out = csv.cached_dh(&key, &secret)?; + APIResult::Ok(out.to_string()) + } + + pub fn computeDh(kind: String, key: String, secret: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; + let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_compute_dh", + "kind", + kind.to_string(), + ) + })?; + let out = csv.compute_dh(&key, &secret)?; + APIResult::Ok(out.to_string()) + } + + pub fn randomBytes(kind: String, len: u32) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_random_bytes", + "kind", + kind.to_string(), + ) + })?; + let out = csv.random_bytes(len); + let out = data_encoding::BASE64URL_NOPAD.encode(&out); + APIResult::Ok(out) + } + + pub fn defaultSaltLength(kind: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_default_salt_length", + "kind", + kind.to_string(), + ) + })?; + let out = csv.default_salt_length(); + APIResult::Ok(out) + } + + pub fn hashPassword(kind: String, password: String, salt: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + let password: Vec = data_encoding::BASE64URL_NOPAD + .decode(password.as_bytes()) + .unwrap(); + let salt: Vec = data_encoding::BASE64URL_NOPAD + .decode(salt.as_bytes()) + .unwrap(); + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_hash_password", + "kind", + kind.to_string(), + ) + })?; + let out = csv.hash_password(&password, &salt)?; + APIResult::Ok(out) + } + + pub fn verifyPassword( + kind: String, + password: String, + password_hash: String, + ) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + let password: Vec = data_encoding::BASE64URL_NOPAD + .decode(password.as_bytes()) + .unwrap(); + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_verify_password", + "kind", + kind.to_string(), + ) + })?; + let out = csv.verify_password(&password, &password_hash)?; + APIResult::Ok(out) + } + + pub fn deriveSharedSecret( + kind: String, + password: String, + salt: String, + ) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + let password: Vec = data_encoding::BASE64URL_NOPAD + .decode(password.as_bytes()) + .unwrap(); + let salt: Vec = data_encoding::BASE64URL_NOPAD + .decode(salt.as_bytes()) + .unwrap(); + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_derive_shared_secret", + "kind", + kind.to_string(), + ) + })?; + let out = csv.derive_shared_secret(&password, &salt)?; + APIResult::Ok(out.to_string()) + } + + pub fn randomNonce(kind: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_random_nonce", + "kind", + kind.to_string(), + ) + })?; + let out = csv.random_nonce(); + APIResult::Ok(out.to_string()) + } + + pub fn randomSharedSecret(kind: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_random_shared_secret", + "kind", + kind.to_string(), + ) + })?; + let out = csv.random_shared_secret(); + APIResult::Ok(out.to_string()) + } + + pub fn generateKeyPair(kind: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_generate_key_pair", + "kind", + kind.to_string(), + ) + })?; + let out = csv.generate_keypair(); + APIResult::Ok(out) + } + + pub fn generateHash(kind: String, data: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .unwrap(); + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_generate_hash", + "kind", + kind.to_string(), + ) + })?; + let out = csv.generate_hash(&data); + APIResult::Ok(out.to_string()) + } + + pub fn validateKeyPair(kind: String, key: String, secret: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; + let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_validate_key_pair", + "kind", + kind.to_string(), + ) + })?; + let out = csv.validate_keypair(&key, &secret); + APIResult::Ok(out) + } + + pub fn validateHash(kind: String, data: String, hash: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .unwrap(); + + let hash: veilid_core::HashDigest = veilid_core::HashDigest::from_str(&hash)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_validate_hash", + "kind", + kind.to_string(), + ) + })?; + let out = csv.validate_hash(&data, &hash); + APIResult::Ok(out) + } + + pub fn distance(kind: String, key1: String, key2: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let key1: veilid_core::CryptoKey = veilid_core::CryptoKey::from_str(&key1)?; + let key2: veilid_core::CryptoKey = veilid_core::CryptoKey::from_str(&key2)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_distance", + "kind", + kind.to_string(), + ) + })?; + let out = csv.distance(&key1, &key2); + APIResult::Ok(out.to_string()) + } + + pub fn sign( + kind: String, + key: String, + secret: String, + data: String, + ) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; + let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?; + + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .unwrap(); + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument("crypto_sign", "kind", kind.to_string()) + })?; + let out = csv.sign(&key, &secret, &data)?; + APIResult::Ok(out.to_string()) + } + + pub fn verify( + kind: String, + key: String, + data: String, + signature: String, + ) -> VeilidAPIResult<()> { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?; + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .unwrap(); + let signature: veilid_core::Signature = veilid_core::Signature::from_str(&signature)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string()) + })?; + csv.verify(&key, &data, &signature)?; + APIRESULT_UNDEFINED + } + + pub fn aeadOverhead(kind: String) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_aead_overhead", + "kind", + kind.to_string(), + ) + })?; + let out = csv.aead_overhead(); + APIResult::Ok(out) + } + + pub fn decryptAead( + kind: String, + body: String, + nonce: String, + shared_secret: String, + associated_data: Option, + ) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let body: Vec = data_encoding::BASE64URL_NOPAD + .decode(body.as_bytes()) + .unwrap(); + + let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce)?; + + let shared_secret: veilid_core::SharedSecret = + veilid_core::SharedSecret::from_str(&shared_secret)?; + + let associated_data: Option> = associated_data.map(|ad| { + data_encoding::BASE64URL_NOPAD + .decode(ad.as_bytes()) + .unwrap() + }); + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_decrypt_aead", + "kind", + kind.to_string(), + ) + })?; + let out = csv.decrypt_aead( + &body, + &nonce, + &shared_secret, + match &associated_data { + Some(ad) => Some(ad.as_slice()), + None => None, + }, + )?; + let out = data_encoding::BASE64URL_NOPAD.encode(&out); + APIResult::Ok(out) + } + + pub fn encryptAead( + kind: String, + body: String, + nonce: String, + shared_secret: String, + associated_data: Option, + ) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let body: Vec = data_encoding::BASE64URL_NOPAD + .decode(body.as_bytes()) + .unwrap(); + + let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce).unwrap(); + + let shared_secret: veilid_core::SharedSecret = + veilid_core::SharedSecret::from_str(&shared_secret).unwrap(); + + let associated_data: Option> = associated_data.map(|ad| { + data_encoding::BASE64URL_NOPAD + .decode(ad.as_bytes()) + .unwrap() + }); + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_encrypt_aead", + "kind", + kind.to_string(), + ) + })?; + let out = csv.encrypt_aead( + &body, + &nonce, + &shared_secret, + match &associated_data { + Some(ad) => Some(ad.as_slice()), + None => None, + }, + )?; + let out = data_encoding::BASE64URL_NOPAD.encode(&out); + APIResult::Ok(out) + } + + pub fn cryptNoAuth( + kind: String, + body: String, + nonce: String, + shared_secret: String, + ) -> VeilidAPIResult { + let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?; + + let mut body: Vec = data_encoding::BASE64URL_NOPAD + .decode(body.as_bytes()) + .unwrap(); + + let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce).unwrap(); + + let shared_secret: veilid_core::SharedSecret = + veilid_core::SharedSecret::from_str(&shared_secret).unwrap(); + + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_crypt_no_auth", + "kind", + kind.to_string(), + ) + })?; + csv.crypt_in_place_no_auth(&mut body, &nonce, &shared_secret); + let out = data_encoding::BASE64URL_NOPAD.encode(&body); + APIResult::Ok(out) + } +} diff --git a/veilid-wasm/src/veilid_routing_context_js.rs b/veilid-wasm/src/veilid_routing_context_js.rs index a03c62e9..8bb3f656 100644 --- a/veilid-wasm/src/veilid_routing_context_js.rs +++ b/veilid-wasm/src/veilid_routing_context_js.rs @@ -13,6 +13,7 @@ impl VeilidRoutingContext { Self { id } } + // Factories pub fn createWithoutPrivacy() -> VeilidAPIResult { let veilid_api = get_veilid_api()?; let routing_context = veilid_api.routing_context(); @@ -45,6 +46,54 @@ impl VeilidRoutingContext { Ok(VeilidRoutingContext { id }) } + // Static methods + pub async fn newPrivateRoute() -> VeilidAPIResult { + let veilid_api = get_veilid_api()?; + + let (route_id, blob) = veilid_api.new_private_route().await?; + + let route_blob = VeilidRouteBlob { route_id, blob }; + APIResult::Ok(route_blob) + } + + pub async fn newCustomPrivateRoute( + stability: Stability, + sequencing: Sequencing, + ) -> VeilidAPIResult { + let veilid_api = get_veilid_api()?; + + let (route_id, blob) = veilid_api + .new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing) + .await?; + + let route_blob = VeilidRouteBlob { route_id, blob }; + APIResult::Ok(route_blob) + } + + pub async fn releasePrivateRoute(routeId: String) -> VeilidAPIResult<()> { + let route_id: veilid_core::RouteId = veilid_core::deserialize_json(&routeId).unwrap(); + let veilid_api = get_veilid_api()?; + veilid_api.release_private_route(route_id)?; + APIRESULT_UNDEFINED + } + + pub async fn appCallReply(callId: String, message: String) -> VeilidAPIResult<()> { + let call_id = match callId.parse() { + Ok(v) => v, + Err(e) => { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + e, "call_id", callId, + )) + } + }; + let veilid_api = get_veilid_api()?; + veilid_api + .app_call_reply(call_id, message.into_bytes()) + .await?; + APIRESULT_UNDEFINED + } + + // Instance methods pub async fn appMessage(&self, target_string: String, message: String) -> VeilidAPIResult<()> { let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); @@ -62,12 +111,7 @@ impl VeilidRoutingContext { APIRESULT_UNDEFINED } - pub async fn appCall( - &self, - id: u32, - target_string: String, - request: String, - ) -> VeilidAPIResult { + pub async fn appCall(&self, target_string: String, request: String) -> VeilidAPIResult { let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&self.id) else { @@ -86,12 +130,15 @@ impl VeilidRoutingContext { APIResult::Ok(answer) } - pub async fn createDhtRecord(&self, schema: JsValue, kind: u32) -> VeilidAPIResult { - let schema: DHTSchema = serde_wasm_bindgen::from_value(schema).unwrap(); - let crypto_kind = if kind == 0 { + pub async fn createDhtRecord( + &self, + schema: DHTSchema, + kind: String, + ) -> VeilidAPIResult { + let crypto_kind = if kind.is_empty() { None } else { - Some(veilid_core::FourCC::from(kind)) + Some(veilid_core::FourCC::from_str(&kind)?) }; let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); @@ -104,18 +151,21 @@ impl VeilidRoutingContext { let dht_record_descriptor = routing_context .create_dht_record(schema, crypto_kind) .await?; - let out = serde_wasm_bindgen::to_value(&dht_record_descriptor).unwrap(); - APIResult::Ok(out) + APIResult::Ok(dht_record_descriptor) } + /// @param {string} writer - Stringified key pair in the form of `key:secret`. pub async fn openDhtRecord( &self, key: String, writer: Option, - ) -> VeilidAPIResult { - let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap(); - let writer: Option = - writer.map(|s| veilid_core::deserialize_json(&s).unwrap()); + ) -> VeilidAPIResult { + let key = TypedKey::from_str(&key).unwrap(); + let writer = match writer { + Some(writer) => Some(KeyPair::from_str(&writer).unwrap()), + _ => None, + }; + let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&self.id) else { @@ -124,12 +174,11 @@ impl VeilidRoutingContext { routing_context.clone() }; let dht_record_descriptor = routing_context.open_dht_record(key, writer).await?; - let out = serde_wasm_bindgen::to_value(&dht_record_descriptor).unwrap(); - APIResult::Ok(out) + APIResult::Ok(dht_record_descriptor) } pub async fn closeDhtRecord(&self, key: String) -> VeilidAPIResult<()> { - let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap(); + let key = TypedKey::from_str(&key).unwrap(); let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&self.id) else { @@ -142,7 +191,7 @@ impl VeilidRoutingContext { } pub async fn deleteDhtRecord(&self, key: String) -> VeilidAPIResult<()> { - let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap(); + let key = TypedKey::from_str(&key).unwrap(); let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&self.id) else { @@ -159,8 +208,8 @@ impl VeilidRoutingContext { key: String, subKey: u32, forceRefresh: bool, - ) -> VeilidAPIResult { - let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap(); + ) -> VeilidAPIResult> { + let key = TypedKey::from_str(&key).unwrap(); let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&self.id) else { @@ -171,17 +220,17 @@ impl VeilidRoutingContext { let res = routing_context .get_dht_value(key, subKey, forceRefresh) .await?; - let out = serde_wasm_bindgen::to_value(&res).unwrap(); - APIResult::Ok(out) + APIResult::Ok(res) } + /// @param {string} data - Base64Url (no padding) encoded data string pub async fn setDhtValue( &self, key: String, subKey: u32, data: String, - ) -> VeilidAPIResult { - let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap(); + ) -> VeilidAPIResult> { + let key = TypedKey::from_str(&key).unwrap(); let data: Vec = data_encoding::BASE64URL_NOPAD .decode(&data.as_bytes()) .unwrap(); @@ -194,8 +243,7 @@ impl VeilidRoutingContext { routing_context.clone() }; let res = routing_context.set_dht_value(key, subKey, data).await?; - let out = serde_wasm_bindgen::to_value(&res).unwrap(); - APIResult::Ok(out) + APIResult::Ok(res) } // pub async fn watchDhtValues( @@ -239,55 +287,3 @@ impl VeilidRoutingContext { // APIResult::Ok(res) // } } - -#[wasm_bindgen()] -pub async fn newPrivateRoute() -> VeilidAPIResult { - let veilid_api = get_veilid_api()?; - - let (route_id, blob) = veilid_api.new_private_route().await?; - - let route_blob = VeilidRouteBlob { route_id, blob }; - let out = serde_wasm_bindgen::to_value(&route_blob).unwrap(); - APIResult::Ok(out) -} - -#[wasm_bindgen()] -pub async fn newCustomPrivateRoute( - stability: Stability, - sequencing: Sequencing, -) -> VeilidAPIResult { - let veilid_api = get_veilid_api()?; - - let (route_id, blob) = veilid_api - .new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing) - .await?; - - let route_blob = VeilidRouteBlob { route_id, blob }; - let out = serde_wasm_bindgen::to_value(&route_blob).unwrap(); - APIResult::Ok(out) -} - -#[wasm_bindgen()] -pub async fn releasePrivateRoute(routeId: String) -> VeilidAPIResult<()> { - let route_id: veilid_core::RouteId = veilid_core::deserialize_json(&routeId).unwrap(); - let veilid_api = get_veilid_api()?; - veilid_api.release_private_route(route_id)?; - APIRESULT_UNDEFINED -} - -#[wasm_bindgen()] -pub async fn appCallReply(callId: String, message: String) -> VeilidAPIResult<()> { - let call_id = match callId.parse() { - Ok(v) => v, - Err(e) => { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( - e, "call_id", callId, - )) - } - }; - let veilid_api = get_veilid_api()?; - veilid_api - .app_call_reply(call_id, message.into_bytes()) - .await?; - APIRESULT_UNDEFINED -} diff --git a/veilid-wasm/src/wasm_helpers.rs b/veilid-wasm/src/wasm_helpers.rs new file mode 100644 index 00000000..2792a228 --- /dev/null +++ b/veilid-wasm/src/wasm_helpers.rs @@ -0,0 +1,21 @@ +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + pub use tsify::*; + pub use wasm_bindgen::prelude::*; + + macro_rules! from_impl_to_jsvalue { + ($name: ident) => { + impl From<$name> for JsValue { + fn from(value: $name) -> Self { + serde_wasm_bindgen::to_value(&value).unwrap() + } + } + } + } + } else { + macro_rules! from_impl_to_jsvalue { + ($name: ident) => {} + } + } +} +pub(crate) use from_impl_to_jsvalue;