Merge branch 'feature/bytes-as-uint8arrays' into 'main'
(wasm) Treat arbitrary byte data as Uint8Array, instead of base64url marshalling. See merge request veilid/veilid!196
This commit is contained in:
commit
38ff6f81c5
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -4155,6 +4155,15 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_bytes"
|
||||||
|
version = "0.11.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_cbor"
|
name = "serde_cbor"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -5314,6 +5323,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde-big-array",
|
"serde-big-array",
|
||||||
"serde-wasm-bindgen 0.6.0",
|
"serde-wasm-bindgen 0.6.0",
|
||||||
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"shell-words",
|
"shell-words",
|
||||||
@ -5521,6 +5531,7 @@ dependencies = [
|
|||||||
"send_wrapper 0.6.0",
|
"send_wrapper 0.6.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-wasm-bindgen 0.6.0",
|
"serde-wasm-bindgen 0.6.0",
|
||||||
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
@ -198,6 +198,9 @@ wasm-bindgen = "0.2.87"
|
|||||||
js-sys = "0.3.64"
|
js-sys = "0.3.64"
|
||||||
wasm-bindgen-futures = "0.4.37"
|
wasm-bindgen-futures = "0.4.37"
|
||||||
send_wrapper = { version = "0.6.0", features = ["futures"] }
|
send_wrapper = { version = "0.6.0", features = ["futures"] }
|
||||||
|
serde_bytes = { version = "0.11", default_features = false, features = [
|
||||||
|
"alloc",
|
||||||
|
] }
|
||||||
tsify = { version = "0.4.5", features = ["js"] }
|
tsify = { version = "0.4.5", features = ["js"] }
|
||||||
serde-wasm-bindgen = "0.6.0"
|
serde-wasm-bindgen = "0.6.0"
|
||||||
|
|
||||||
|
@ -215,7 +215,6 @@ impl VeilidAPIError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
|
||||||
pub type VeilidAPIResult<T> = Result<T, VeilidAPIError>;
|
pub type VeilidAPIResult<T> = Result<T, VeilidAPIError>;
|
||||||
|
|
||||||
impl From<std::io::Error> for VeilidAPIError {
|
impl From<std::io::Error> for VeilidAPIError {
|
||||||
|
@ -9,9 +9,13 @@ pub struct VeilidAppMessage {
|
|||||||
#[cfg_attr(target_arch = "wasm32", tsify(optional, type = "string"))]
|
#[cfg_attr(target_arch = "wasm32", tsify(optional, type = "string"))]
|
||||||
sender: Option<TypedKey>,
|
sender: Option<TypedKey>,
|
||||||
|
|
||||||
#[serde(with = "as_human_base64")]
|
#[cfg_attr(not(target_arch = "wasm32"), serde(with = "as_human_base64"))]
|
||||||
#[schemars(with = "String")]
|
#[schemars(with = "String")]
|
||||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
#[cfg_attr(
|
||||||
|
target_arch = "wasm32",
|
||||||
|
serde(with = "serde_bytes"),
|
||||||
|
tsify(type = "Uint8Array")
|
||||||
|
)]
|
||||||
message: Vec<u8>,
|
message: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,8 +44,13 @@ pub struct VeilidAppCall {
|
|||||||
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
#[cfg_attr(target_arch = "wasm32", tsify(optional))]
|
||||||
sender: Option<TypedKey>,
|
sender: Option<TypedKey>,
|
||||||
|
|
||||||
#[serde(with = "as_human_base64")]
|
#[cfg_attr(not(target_arch = "wasm32"), serde(with = "as_human_base64"))]
|
||||||
#[schemars(with = "String")]
|
#[schemars(with = "String")]
|
||||||
|
#[cfg_attr(
|
||||||
|
target_arch = "wasm32",
|
||||||
|
serde(with = "serde_bytes"),
|
||||||
|
tsify(type = "Uint8Array")
|
||||||
|
)]
|
||||||
message: Vec<u8>,
|
message: Vec<u8>,
|
||||||
|
|
||||||
#[serde(with = "as_human_string")]
|
#[serde(with = "as_human_string")]
|
||||||
|
@ -8,9 +8,13 @@ pub struct ValueData {
|
|||||||
seq: ValueSeqNum,
|
seq: ValueSeqNum,
|
||||||
|
|
||||||
/// The contents of a DHT Record
|
/// The contents of a DHT Record
|
||||||
#[serde(with = "as_human_base64")]
|
#[cfg_attr(not(target_arch = "wasm32"), serde(with = "as_human_base64"))]
|
||||||
#[schemars(with = "String")]
|
#[schemars(with = "String")]
|
||||||
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
|
#[cfg_attr(
|
||||||
|
target_arch = "wasm32",
|
||||||
|
serde(with = "serde_bytes"),
|
||||||
|
tsify(type = "Uint8Array")
|
||||||
|
)]
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
|
|
||||||
/// The public identity key of the writer of the data
|
/// The public identity key of the writer of the data
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
|
||||||
pub type ConfigCallbackReturn = VeilidAPIResult<Box<dyn core::any::Any + Send>>;
|
pub type ConfigCallbackReturn = VeilidAPIResult<Box<dyn core::any::Any + Send>>;
|
||||||
#[cfg_attr(target_arch = "wasm32", declare)]
|
|
||||||
pub type ConfigCallback = Arc<dyn Fn(String) -> ConfigCallbackReturn + Send + Sync>;
|
pub type ConfigCallback = Arc<dyn Fn(String) -> ConfigCallbackReturn + Send + Sync>;
|
||||||
|
|
||||||
/// Enable and configure HTTPS access to the Veilid node
|
/// Enable and configure HTTPS access to the Veilid node
|
||||||
|
@ -28,6 +28,9 @@ cfg-if = "^1"
|
|||||||
wasm-bindgen-futures = "^0"
|
wasm-bindgen-futures = "^0"
|
||||||
js-sys = "^0"
|
js-sys = "^0"
|
||||||
serde_json = "^1"
|
serde_json = "^1"
|
||||||
|
serde_bytes = { version = "0.11", default_features = false, features = [
|
||||||
|
"alloc",
|
||||||
|
] }
|
||||||
serde = "^1"
|
serde = "^1"
|
||||||
lazy_static = "^1"
|
lazy_static = "^1"
|
||||||
send_wrapper = "^0"
|
send_wrapper = "^0"
|
||||||
|
@ -58,7 +58,7 @@ impl VeilidCrypto {
|
|||||||
APIResult::Ok(out.to_string())
|
APIResult::Ok(out.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn randomBytes(kind: String, len: u32) -> APIResult<String> {
|
pub fn randomBytes(kind: String, len: u32) -> APIResult<Box<[u8]>> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
@ -71,7 +71,7 @@ impl VeilidCrypto {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let out = crypto_system.random_bytes(len);
|
let out = crypto_system.random_bytes(len);
|
||||||
let out = data_encoding::BASE64URL_NOPAD.encode(&out);
|
let out = out.into_boxed_slice();
|
||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,10 +91,8 @@ impl VeilidCrypto {
|
|||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hashPassword(kind: String, password: String, salt: String) -> APIResult<String> {
|
pub fn hashPassword(kind: String, password: Box<[u8]>, salt: Box<[u8]>) -> APIResult<String> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
let password = unmarshall(password)?;
|
|
||||||
let salt = unmarshall(salt)?;
|
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let crypto = veilid_api.crypto()?;
|
let crypto = veilid_api.crypto()?;
|
||||||
@ -111,11 +109,10 @@ impl VeilidCrypto {
|
|||||||
|
|
||||||
pub fn verifyPassword(
|
pub fn verifyPassword(
|
||||||
kind: String,
|
kind: String,
|
||||||
password: String,
|
password: Box<[u8]>,
|
||||||
password_hash: String,
|
password_hash: String,
|
||||||
) -> APIResult<bool> {
|
) -> APIResult<bool> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
let password = unmarshall(password)?;
|
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let crypto = veilid_api.crypto()?;
|
let crypto = veilid_api.crypto()?;
|
||||||
@ -130,10 +127,12 @@ impl VeilidCrypto {
|
|||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deriveSharedSecret(kind: String, password: String, salt: String) -> APIResult<String> {
|
pub fn deriveSharedSecret(
|
||||||
|
kind: String,
|
||||||
|
password: Box<[u8]>,
|
||||||
|
salt: Box<[u8]>,
|
||||||
|
) -> APIResult<String> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
let password = unmarshall(password)?;
|
|
||||||
let salt = unmarshall(salt)?;
|
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let crypto = veilid_api.crypto()?;
|
let crypto = veilid_api.crypto()?;
|
||||||
@ -182,7 +181,7 @@ impl VeilidCrypto {
|
|||||||
|
|
||||||
pub fn verifySignatures(
|
pub fn verifySignatures(
|
||||||
node_ids: StringArray,
|
node_ids: StringArray,
|
||||||
data: String,
|
data: Box<[u8]>,
|
||||||
signatures: StringArray,
|
signatures: StringArray,
|
||||||
) -> VeilidAPIResult<StringArray> {
|
) -> VeilidAPIResult<StringArray> {
|
||||||
let node_ids = into_unchecked_string_vec(node_ids);
|
let node_ids = into_unchecked_string_vec(node_ids);
|
||||||
@ -199,8 +198,6 @@ impl VeilidCrypto {
|
|||||||
})
|
})
|
||||||
.collect::<APIResult<Vec<TypedKey>>>()?;
|
.collect::<APIResult<Vec<TypedKey>>>()?;
|
||||||
|
|
||||||
let data: Vec<u8> = unmarshall(data)?;
|
|
||||||
|
|
||||||
let typed_signatures = into_unchecked_string_vec(signatures);
|
let typed_signatures = into_unchecked_string_vec(signatures);
|
||||||
let typed_signatures: Vec<TypedSignature> = typed_signatures
|
let typed_signatures: Vec<TypedSignature> = typed_signatures
|
||||||
.iter()
|
.iter()
|
||||||
@ -226,9 +223,7 @@ impl VeilidCrypto {
|
|||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generateSignatures(data: String, key_pairs: StringArray) -> APIResult<StringArray> {
|
pub fn generateSignatures(data: Box<[u8]>, key_pairs: StringArray) -> APIResult<StringArray> {
|
||||||
let data = unmarshall(data)?;
|
|
||||||
|
|
||||||
let key_pairs = into_unchecked_string_vec(key_pairs);
|
let key_pairs = into_unchecked_string_vec(key_pairs);
|
||||||
let key_pairs: Vec<TypedKeyPair> = key_pairs
|
let key_pairs: Vec<TypedKeyPair> = key_pairs
|
||||||
.iter()
|
.iter()
|
||||||
@ -269,11 +264,9 @@ impl VeilidCrypto {
|
|||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generateHash(kind: String, data: String) -> APIResult<String> {
|
pub fn generateHash(kind: String, data: Box<[u8]>) -> APIResult<String> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
|
|
||||||
let data = unmarshall(data)?;
|
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let crypto = veilid_api.crypto()?;
|
let crypto = veilid_api.crypto()?;
|
||||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||||
@ -306,11 +299,9 @@ impl VeilidCrypto {
|
|||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validateHash(kind: String, data: String, hash: String) -> APIResult<bool> {
|
pub fn validateHash(kind: String, data: Box<[u8]>, hash: String) -> APIResult<bool> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
|
|
||||||
let data = unmarshall(data)?;
|
|
||||||
|
|
||||||
let hash: veilid_core::HashDigest = veilid_core::HashDigest::from_str(&hash)?;
|
let hash: veilid_core::HashDigest = veilid_core::HashDigest::from_str(&hash)?;
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
@ -345,14 +336,12 @@ impl VeilidCrypto {
|
|||||||
APIResult::Ok(out.to_string())
|
APIResult::Ok(out.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(kind: String, key: String, secret: String, data: String) -> APIResult<String> {
|
pub fn sign(kind: String, key: String, secret: String, data: Box<[u8]>) -> APIResult<String> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
|
|
||||||
let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?;
|
let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?;
|
||||||
let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?;
|
let secret: veilid_core::SecretKey = veilid_core::SecretKey::from_str(&secret)?;
|
||||||
|
|
||||||
let data = unmarshall(data)?;
|
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let crypto = veilid_api.crypto()?;
|
let crypto = veilid_api.crypto()?;
|
||||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||||
@ -362,11 +351,10 @@ impl VeilidCrypto {
|
|||||||
APIResult::Ok(out.to_string())
|
APIResult::Ok(out.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(kind: String, key: String, data: String, signature: String) -> APIResult<()> {
|
pub fn verify(kind: String, key: String, data: Box<[u8]>, signature: String) -> APIResult<()> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
|
|
||||||
let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?;
|
let key: veilid_core::PublicKey = veilid_core::PublicKey::from_str(&key)?;
|
||||||
let data = unmarshall(data)?;
|
|
||||||
let signature: veilid_core::Signature = veilid_core::Signature::from_str(&signature)?;
|
let signature: veilid_core::Signature = veilid_core::Signature::from_str(&signature)?;
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
@ -396,24 +384,18 @@ impl VeilidCrypto {
|
|||||||
|
|
||||||
pub fn decryptAead(
|
pub fn decryptAead(
|
||||||
kind: String,
|
kind: String,
|
||||||
body: String,
|
body: Box<[u8]>,
|
||||||
nonce: String,
|
nonce: String,
|
||||||
shared_secret: String,
|
shared_secret: String,
|
||||||
associated_data: Option<String>,
|
associated_data: Option<Box<[u8]>>,
|
||||||
) -> APIResult<String> {
|
) -> APIResult<Box<[u8]>> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
|
|
||||||
let body = unmarshall(body)?;
|
|
||||||
|
|
||||||
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce)?;
|
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce)?;
|
||||||
|
|
||||||
let shared_secret: veilid_core::SharedSecret =
|
let shared_secret: veilid_core::SharedSecret =
|
||||||
veilid_core::SharedSecret::from_str(&shared_secret)?;
|
veilid_core::SharedSecret::from_str(&shared_secret)?;
|
||||||
|
|
||||||
let associated_data = associated_data
|
|
||||||
.map(unmarshall)
|
|
||||||
.map_or(APIResult::Ok(None), |r| r.map(Some))?;
|
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let crypto = veilid_api.crypto()?;
|
let crypto = veilid_api.crypto()?;
|
||||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||||
@ -428,34 +410,28 @@ impl VeilidCrypto {
|
|||||||
&nonce,
|
&nonce,
|
||||||
&shared_secret,
|
&shared_secret,
|
||||||
match &associated_data {
|
match &associated_data {
|
||||||
Some(ad) => Some(ad.as_slice()),
|
Some(ad) => Some(ad),
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
let out = data_encoding::BASE64URL_NOPAD.encode(&out);
|
let out = out.into_boxed_slice();
|
||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encryptAead(
|
pub fn encryptAead(
|
||||||
kind: String,
|
kind: String,
|
||||||
body: String,
|
body: Box<[u8]>,
|
||||||
nonce: String,
|
nonce: String,
|
||||||
shared_secret: String,
|
shared_secret: String,
|
||||||
associated_data: Option<String>,
|
associated_data: Option<Box<[u8]>>,
|
||||||
) -> APIResult<String> {
|
) -> APIResult<Box<[u8]>> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
|
|
||||||
let body = unmarshall(body)?;
|
|
||||||
|
|
||||||
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce)?;
|
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce)?;
|
||||||
|
|
||||||
let shared_secret: veilid_core::SharedSecret =
|
let shared_secret: veilid_core::SharedSecret =
|
||||||
veilid_core::SharedSecret::from_str(&shared_secret)?;
|
veilid_core::SharedSecret::from_str(&shared_secret)?;
|
||||||
|
|
||||||
let associated_data: Option<Vec<u8>> = associated_data
|
|
||||||
.map(unmarshall)
|
|
||||||
.map_or(APIResult::Ok(None), |r| r.map(Some))?;
|
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let crypto = veilid_api.crypto()?;
|
let crypto = veilid_api.crypto()?;
|
||||||
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
let crypto_system = crypto.get(kind).ok_or_else(|| {
|
||||||
@ -470,24 +446,21 @@ impl VeilidCrypto {
|
|||||||
&nonce,
|
&nonce,
|
||||||
&shared_secret,
|
&shared_secret,
|
||||||
match &associated_data {
|
match &associated_data {
|
||||||
Some(ad) => Some(ad.as_slice()),
|
Some(ad) => Some(ad),
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
let out = data_encoding::BASE64URL_NOPAD.encode(&out);
|
APIResult::Ok(out.into_boxed_slice())
|
||||||
APIResult::Ok(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cryptNoAuth(
|
pub fn cryptNoAuth(
|
||||||
kind: String,
|
kind: String,
|
||||||
body: String,
|
mut body: Box<[u8]>,
|
||||||
nonce: String,
|
nonce: String,
|
||||||
shared_secret: String,
|
shared_secret: String,
|
||||||
) -> APIResult<String> {
|
) -> APIResult<Box<[u8]>> {
|
||||||
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
let kind: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&kind)?;
|
||||||
|
|
||||||
let mut body = unmarshall(body)?;
|
|
||||||
|
|
||||||
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce)?;
|
let nonce: veilid_core::Nonce = veilid_core::Nonce::from_str(&nonce)?;
|
||||||
|
|
||||||
let shared_secret: veilid_core::SharedSecret =
|
let shared_secret: veilid_core::SharedSecret =
|
||||||
@ -503,7 +476,6 @@ impl VeilidCrypto {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
crypto_system.crypt_in_place_no_auth(&mut body, &nonce, &shared_secret);
|
crypto_system.crypt_in_place_no_auth(&mut body, &nonce, &shared_secret);
|
||||||
let out = data_encoding::BASE64URL_NOPAD.encode(&body);
|
APIResult::Ok(body)
|
||||||
APIResult::Ok(out)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,8 +83,8 @@ impl VeilidRoutingContext {
|
|||||||
///
|
///
|
||||||
/// * `call_id` - specifies which call to reply to, and it comes from a VeilidUpdate::AppCall, specifically the VeilidAppCall::id() value.
|
/// * `call_id` - specifies which call to reply to, and it comes from a VeilidUpdate::AppCall, specifically the VeilidAppCall::id() value.
|
||||||
/// * `message` - is an answer blob to be returned by the remote node's RoutingContext::app_call() function, and may be up to 32768 bytes
|
/// * `message` - is an answer blob to be returned by the remote node's RoutingContext::app_call() function, and may be up to 32768 bytes
|
||||||
pub async fn appCallReply(call_id: String, message: String) -> APIResult<()> {
|
pub async fn appCallReply(call_id: String, message: Box<[u8]>) -> APIResult<()> {
|
||||||
let message = unmarshall(message)?;
|
let message = message.into_vec();
|
||||||
let call_id = match call_id.parse() {
|
let call_id = match call_id.parse() {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -148,10 +148,9 @@ impl VeilidRoutingContext {
|
|||||||
/// @param {string} target - can be either a direct node id or a private route.
|
/// @param {string} target - can be either a direct node id or a private route.
|
||||||
/// @param {string} message - an arbitrary message blob of up to `32768` bytes.
|
/// @param {string} message - an arbitrary message blob of up to `32768` bytes.
|
||||||
#[wasm_bindgen(skip_jsdoc)]
|
#[wasm_bindgen(skip_jsdoc)]
|
||||||
pub async fn appMessage(&self, target_string: String, message: String) -> APIResult<()> {
|
pub async fn appMessage(&self, target_string: String, message: Box<[u8]>) -> APIResult<()> {
|
||||||
let routing_context = self.getRoutingContext()?;
|
let routing_context = self.getRoutingContext()?;
|
||||||
let message = unmarshall(message)?;
|
let message = message.into_vec();
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let target = veilid_api.parse_as_target(target_string).await?;
|
let target = veilid_api.parse_as_target(target_string).await?;
|
||||||
routing_context.app_message(target, message).await?;
|
routing_context.app_message(target, message).await?;
|
||||||
@ -162,18 +161,22 @@ impl VeilidRoutingContext {
|
|||||||
///
|
///
|
||||||
/// Veilid apps may use this for arbitrary message passing.
|
/// Veilid apps may use this for arbitrary message passing.
|
||||||
///
|
///
|
||||||
/// @param {string} target_string - can be either a direct node id or a private route, base64Url encoded.
|
/// @param {string} target_string - can be either a direct node id or a private route.
|
||||||
/// @param {string} message - an arbitrary message blob of up to `32768` bytes, base64Url encoded.
|
/// @param {Uint8Array} message - an arbitrary message blob of up to `32768` bytes.
|
||||||
/// @returns an answer blob of up to `32768` bytes, base64Url encoded.
|
/// @returns {Uint8Array} an answer blob of up to `32768` bytes.
|
||||||
#[wasm_bindgen(skip_jsdoc)]
|
#[wasm_bindgen(skip_jsdoc)]
|
||||||
pub async fn appCall(&self, target_string: String, request: String) -> APIResult<String> {
|
pub async fn appCall(
|
||||||
let request: Vec<u8> = unmarshall(request)?;
|
&self,
|
||||||
|
target_string: String,
|
||||||
|
request: Box<[u8]>,
|
||||||
|
) -> APIResult<Uint8Array> {
|
||||||
|
let request: Vec<u8> = request.into_vec();
|
||||||
let routing_context = self.getRoutingContext()?;
|
let routing_context = self.getRoutingContext()?;
|
||||||
|
|
||||||
let veilid_api = get_veilid_api()?;
|
let veilid_api = get_veilid_api()?;
|
||||||
let target = veilid_api.parse_as_target(target_string).await?;
|
let target = veilid_api.parse_as_target(target_string).await?;
|
||||||
let answer = routing_context.app_call(target, request).await?;
|
let answer = routing_context.app_call(target, request).await?;
|
||||||
let answer = marshall(&answer);
|
let answer = Uint8Array::from(answer.as_slice());
|
||||||
APIResult::Ok(answer)
|
APIResult::Ok(answer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +253,7 @@ impl VeilidRoutingContext {
|
|||||||
/// May pull the latest value from the network, but by settings 'force_refresh' you can force a network data refresh.
|
/// May pull the latest value from the network, but by settings 'force_refresh' you can force a network data refresh.
|
||||||
///
|
///
|
||||||
/// Returns `undefined` if the value subkey has not yet been set.
|
/// Returns `undefined` if the value subkey has not yet been set.
|
||||||
/// Returns base64Url encoded `data` if the value subkey has valid data.
|
/// Returns a Uint8Array of `data` if the value subkey has valid data.
|
||||||
pub async fn getDhtValue(
|
pub async fn getDhtValue(
|
||||||
&self,
|
&self,
|
||||||
key: String,
|
key: String,
|
||||||
@ -268,15 +271,15 @@ impl VeilidRoutingContext {
|
|||||||
/// Pushes a changed subkey value to the network
|
/// Pushes a changed subkey value to the network
|
||||||
///
|
///
|
||||||
/// Returns `undefined` if the value was successfully put.
|
/// Returns `undefined` if the value was successfully put.
|
||||||
/// Returns base64Url encoded `data` if the value put was older than the one available on the network.
|
/// Returns a Uint8Array of `data` if the value put was older than the one available on the network.
|
||||||
pub async fn setDhtValue(
|
pub async fn setDhtValue(
|
||||||
&self,
|
&self,
|
||||||
key: String,
|
key: String,
|
||||||
subKey: u32,
|
subKey: u32,
|
||||||
data: String,
|
data: Box<[u8]>,
|
||||||
) -> APIResult<Option<ValueData>> {
|
) -> APIResult<Option<ValueData>> {
|
||||||
let key = TypedKey::from_str(&key)?;
|
let key = TypedKey::from_str(&key)?;
|
||||||
let data = unmarshall(data)?;
|
let data = data.into_vec();
|
||||||
|
|
||||||
let routing_context = self.getRoutingContext()?;
|
let routing_context = self.getRoutingContext()?;
|
||||||
let res = routing_context.set_dht_value(key, subKey, data).await?;
|
let res = routing_context.set_dht_value(key, subKey, data).await?;
|
||||||
|
@ -63,22 +63,24 @@ impl VeilidTableDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read a key from a column in the TableDB immediately.
|
/// Read a key from a column in the TableDB immediately.
|
||||||
pub async fn load(&mut self, columnId: u32, key: String) -> APIResult<Option<String>> {
|
pub async fn load(&mut self, columnId: u32, key: Box<[u8]>) -> APIResult<Option<Uint8Array>> {
|
||||||
self.ensureOpen().await;
|
self.ensureOpen().await;
|
||||||
let key = unmarshall(key)?;
|
|
||||||
let table_db = self.getTableDB()?;
|
let table_db = self.getTableDB()?;
|
||||||
|
|
||||||
let out = table_db.load(columnId, &key).await?;
|
let out = table_db.load(columnId, &key).await?;
|
||||||
let out = out.map(|out| marshall(&out));
|
let out = out.map(|out| Uint8Array::from(out.as_slice()));
|
||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a key with a value in a column in the TableDB.
|
/// Store a key with a value in a column in the TableDB.
|
||||||
/// Performs a single transaction immediately.
|
/// Performs a single transaction immediately.
|
||||||
pub async fn store(&mut self, columnId: u32, key: String, value: String) -> APIResult<()> {
|
pub async fn store(
|
||||||
|
&mut self,
|
||||||
|
columnId: u32,
|
||||||
|
key: Box<[u8]>,
|
||||||
|
value: Box<[u8]>,
|
||||||
|
) -> APIResult<()> {
|
||||||
self.ensureOpen().await;
|
self.ensureOpen().await;
|
||||||
let key = unmarshall(key)?;
|
|
||||||
let value = unmarshall(value)?;
|
|
||||||
let table_db = self.getTableDB()?;
|
let table_db = self.getTableDB()?;
|
||||||
|
|
||||||
table_db.store(columnId, &key, &value).await?;
|
table_db.store(columnId, &key, &value).await?;
|
||||||
@ -86,26 +88,29 @@ impl VeilidTableDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Delete key with from a column in the TableDB.
|
/// Delete key with from a column in the TableDB.
|
||||||
pub async fn delete(&mut self, columnId: u32, key: String) -> APIResult<Option<String>> {
|
pub async fn delete(&mut self, columnId: u32, key: Box<[u8]>) -> APIResult<Option<Uint8Array>> {
|
||||||
self.ensureOpen().await;
|
self.ensureOpen().await;
|
||||||
let key = unmarshall(key)?;
|
|
||||||
let table_db = self.getTableDB()?;
|
let table_db = self.getTableDB()?;
|
||||||
|
|
||||||
let out = table_db.delete(columnId, &key).await?;
|
let out = table_db.delete(columnId, &key).await?;
|
||||||
let out = out.map(|out| marshall(&out));
|
let out = out.map(|out| Uint8Array::from(out.as_slice()));
|
||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the list of keys in a column of the TableDB.
|
/// Get the list of keys in a column of the TableDB.
|
||||||
///
|
///
|
||||||
/// Returns an array of base64Url encoded keys.
|
/// Returns an array of Uint8Array keys.
|
||||||
pub async fn getKeys(&mut self, columnId: u32) -> APIResult<StringArray> {
|
pub async fn getKeys(&mut self, columnId: u32) -> APIResult<Uint8ArrayArray> {
|
||||||
self.ensureOpen().await;
|
self.ensureOpen().await;
|
||||||
let table_db = self.getTableDB()?;
|
let table_db = self.getTableDB()?;
|
||||||
|
|
||||||
let keys = table_db.clone().get_keys(columnId).await?;
|
let keys = table_db.clone().get_keys(columnId).await?;
|
||||||
let out: Vec<String> = keys.into_iter().map(|k| marshall(&k)).collect();
|
let out: Vec<Uint8Array> = keys
|
||||||
let out = into_unchecked_string_array(out);
|
.into_iter()
|
||||||
|
.map(|k| Uint8Array::from(k.as_slice()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let out = into_unchecked_uint8array_array(out);
|
||||||
|
|
||||||
APIResult::Ok(out)
|
APIResult::Ok(out)
|
||||||
}
|
}
|
||||||
@ -164,16 +169,13 @@ impl VeilidTableDBTransaction {
|
|||||||
|
|
||||||
/// Store a key with a value in a column in the TableDB.
|
/// Store a key with a value in a column in the TableDB.
|
||||||
/// Does not modify TableDB until `.commit()` is called.
|
/// Does not modify TableDB until `.commit()` is called.
|
||||||
pub fn store(&self, col: u32, key: String, value: String) -> APIResult<()> {
|
pub fn store(&self, col: u32, key: Box<[u8]>, value: Box<[u8]>) -> APIResult<()> {
|
||||||
let key = unmarshall(key)?;
|
|
||||||
let value = unmarshall(value)?;
|
|
||||||
let transaction = self.getTransaction()?;
|
let transaction = self.getTransaction()?;
|
||||||
transaction.store(col, &key, &value)
|
transaction.store(col, &key, &value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete key with from a column in the TableDB
|
/// Delete key with from a column in the TableDB
|
||||||
pub fn deleteKey(&self, col: u32, key: String) -> APIResult<()> {
|
pub fn deleteKey(&self, col: u32, key: Box<[u8]>) -> APIResult<()> {
|
||||||
let key = unmarshall(key)?;
|
|
||||||
let transaction = self.getTransaction()?;
|
let transaction = self.getTransaction()?;
|
||||||
transaction.delete(col, &key)
|
transaction.delete(col, &key)
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,19 @@ pub(crate) fn into_unchecked_string_array(items: Vec<String>) -> StringArray {
|
|||||||
.unchecked_into::<StringArray>() // TODO: can I do this a better way?
|
.unchecked_into::<StringArray>() // TODO: can I do this a better way?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(typescript_type = "Uint8Array[]")]
|
||||||
|
pub type Uint8ArrayArray;
|
||||||
|
}
|
||||||
|
/// Convert a `Vec<Uint8Array>` into a `js_sys::Array` with the type of `Uint8Array[]`
|
||||||
|
pub(crate) fn into_unchecked_uint8array_array(items: Vec<Uint8Array>) -> Uint8ArrayArray {
|
||||||
|
items
|
||||||
|
.iter()
|
||||||
|
.collect::<js_sys::Array>()
|
||||||
|
.unchecked_into::<Uint8ArrayArray>() // TODO: can I do this a better way?
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert a StringArray (`js_sys::Array` with the type of `string[]`) into `Vec<String>`
|
/// Convert a StringArray (`js_sys::Array` with the type of `string[]`) into `Vec<String>`
|
||||||
pub(crate) fn into_unchecked_string_vec(items: StringArray) -> Vec<String> {
|
pub(crate) fn into_unchecked_string_vec(items: StringArray) -> Vec<String> {
|
||||||
items
|
items
|
||||||
|
2
veilid-wasm/tests/.gitignore
vendored
2
veilid-wasm/tests/.gitignore
vendored
@ -1,2 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
veilid-wasm-pkg
|
coverage
|
173
veilid-wasm/tests/src/VeilidRoutingContext.test.ts
Normal file
173
veilid-wasm/tests/src/VeilidRoutingContext.test.ts
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import { expect } from '@wdio/globals';
|
||||||
|
|
||||||
|
import {
|
||||||
|
veilidCoreInitConfig,
|
||||||
|
veilidCoreStartupConfig,
|
||||||
|
} from './utils/veilid-config';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DHTRecordDescriptor,
|
||||||
|
VeilidRoutingContext,
|
||||||
|
veilidClient,
|
||||||
|
veilidCrypto,
|
||||||
|
} from 'veilid-wasm';
|
||||||
|
import { textEncoder, textDecoder } from './utils/marshalling-utils';
|
||||||
|
import { waitForMs } from './utils/wait-utils';
|
||||||
|
|
||||||
|
describe('VeilidRoutingContext', () => {
|
||||||
|
before('veilid startup', async () => {
|
||||||
|
veilidClient.initializeCore(veilidCoreInitConfig);
|
||||||
|
await veilidClient.startupCore((_update) => {
|
||||||
|
// if (_update.kind === 'Log') {
|
||||||
|
// console.log(_update.message);
|
||||||
|
// }
|
||||||
|
}, JSON.stringify(veilidCoreStartupConfig));
|
||||||
|
await veilidClient.attach();
|
||||||
|
await waitForMs(2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
after('veilid shutdown', async () => {
|
||||||
|
await veilidClient.detach();
|
||||||
|
await veilidClient.shutdownCore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructors', () => {
|
||||||
|
it('should create using .create()', async () => {
|
||||||
|
const routingContext = VeilidRoutingContext.create();
|
||||||
|
expect(routingContext instanceof VeilidRoutingContext).toBe(true);
|
||||||
|
|
||||||
|
routingContext.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create using new', async () => {
|
||||||
|
const routingContext = new VeilidRoutingContext();
|
||||||
|
expect(routingContext instanceof VeilidRoutingContext).toBe(true);
|
||||||
|
|
||||||
|
routingContext.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create with privacy', async () => {
|
||||||
|
const routingContext = VeilidRoutingContext.create().withPrivacy();
|
||||||
|
expect(routingContext instanceof VeilidRoutingContext).toBe(true);
|
||||||
|
|
||||||
|
routingContext.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create with custom privacy', async () => {
|
||||||
|
const routingContext = VeilidRoutingContext.create().withCustomPrivacy({
|
||||||
|
Safe: {
|
||||||
|
hop_count: 2,
|
||||||
|
sequencing: 'EnsureOrdered',
|
||||||
|
stability: 'Reliable',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(routingContext instanceof VeilidRoutingContext).toBe(true);
|
||||||
|
|
||||||
|
routingContext.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create with sequencing', async () => {
|
||||||
|
const routingContext =
|
||||||
|
VeilidRoutingContext.create().withSequencing('EnsureOrdered');
|
||||||
|
expect(routingContext instanceof VeilidRoutingContext).toBe(true);
|
||||||
|
|
||||||
|
routingContext.free();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('operations', () => {
|
||||||
|
let routingContext: VeilidRoutingContext;
|
||||||
|
|
||||||
|
before('create routing context', () => {
|
||||||
|
routingContext = VeilidRoutingContext.create()
|
||||||
|
.withPrivacy()
|
||||||
|
.withSequencing('EnsureOrdered');
|
||||||
|
});
|
||||||
|
|
||||||
|
after('free routing context', () => {
|
||||||
|
routingContext.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('DHT kitchen sink', async () => {
|
||||||
|
let dhtRecord: DHTRecordDescriptor;
|
||||||
|
const data = '🚀 This example DHT data with unicode a Ā 𐀀 文 🚀';
|
||||||
|
|
||||||
|
before('create dht record', async () => {
|
||||||
|
const bestKind = veilidCrypto.bestCryptoKind();
|
||||||
|
dhtRecord = await routingContext.createDhtRecord(
|
||||||
|
{
|
||||||
|
kind: 'DFLT',
|
||||||
|
o_cnt: 1,
|
||||||
|
},
|
||||||
|
bestKind
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(dhtRecord.key).toBeDefined();
|
||||||
|
expect(dhtRecord.owner).toBeDefined();
|
||||||
|
expect(dhtRecord.owner_secret).toBeDefined();
|
||||||
|
expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
after('free dht record', async () => {
|
||||||
|
await routingContext.closeDhtRecord(dhtRecord.key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set value', async () => {
|
||||||
|
const setValueRes = await routingContext.setDhtValue(
|
||||||
|
dhtRecord.key,
|
||||||
|
0,
|
||||||
|
textEncoder.encode(data)
|
||||||
|
);
|
||||||
|
expect(setValueRes).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get value with force refresh', async () => {
|
||||||
|
const getValueRes = await routingContext.getDhtValue(
|
||||||
|
dhtRecord.key,
|
||||||
|
0,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
expect(getValueRes?.data).toBeDefined();
|
||||||
|
expect(textDecoder.decode(getValueRes?.data)).toBe(data);
|
||||||
|
|
||||||
|
expect(getValueRes?.writer).toBe(dhtRecord.owner);
|
||||||
|
expect(getValueRes?.seq).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open readonly record', async () => {
|
||||||
|
await routingContext.closeDhtRecord(dhtRecord.key);
|
||||||
|
|
||||||
|
const readonlyDhtRecord = await routingContext.openDhtRecord(
|
||||||
|
dhtRecord.key
|
||||||
|
);
|
||||||
|
expect(readonlyDhtRecord).toBeDefined();
|
||||||
|
|
||||||
|
const setValueRes = routingContext.setDhtValue(
|
||||||
|
dhtRecord.key,
|
||||||
|
0,
|
||||||
|
textEncoder.encode(data)
|
||||||
|
);
|
||||||
|
await expect(setValueRes).rejects.toEqual({
|
||||||
|
kind: 'Generic',
|
||||||
|
message: 'value is not writable',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open writable record', async () => {
|
||||||
|
await routingContext.closeDhtRecord(dhtRecord.key);
|
||||||
|
|
||||||
|
const writeableDhtRecord = await routingContext.openDhtRecord(
|
||||||
|
dhtRecord.key,
|
||||||
|
`${dhtRecord.owner}:${dhtRecord.owner_secret}`
|
||||||
|
);
|
||||||
|
expect(writeableDhtRecord).toBeDefined();
|
||||||
|
const setValueRes = await routingContext.setDhtValue(
|
||||||
|
dhtRecord.key,
|
||||||
|
0,
|
||||||
|
textEncoder.encode(`${data}👋`)
|
||||||
|
);
|
||||||
|
expect(setValueRes).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -6,7 +6,7 @@ import {
|
|||||||
} from './utils/veilid-config';
|
} from './utils/veilid-config';
|
||||||
|
|
||||||
import { VeilidTableDB, veilidClient } from 'veilid-wasm';
|
import { VeilidTableDB, veilidClient } from 'veilid-wasm';
|
||||||
import { marshall, unmarshall } from './utils/marshalling-utils';
|
import { textEncoder, textDecoder } from './utils/marshalling-utils';
|
||||||
|
|
||||||
const TABLE_NAME = 'some-table';
|
const TABLE_NAME = 'some-table';
|
||||||
const TABLE_COLS = 1;
|
const TABLE_COLS = 1;
|
||||||
@ -57,18 +57,22 @@ describe('VeilidTable', () => {
|
|||||||
const value = 'test value with unicode 🚀';
|
const value = 'test value with unicode 🚀';
|
||||||
|
|
||||||
it('should store value', async () => {
|
it('should store value', async () => {
|
||||||
await table.store(0, marshall(key), marshall(value));
|
await table.store(
|
||||||
|
0,
|
||||||
|
textEncoder.encode(key),
|
||||||
|
textEncoder.encode(value)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load value', async () => {
|
it('should load value', async () => {
|
||||||
const storedValue = await table.load(0, marshall(key));
|
const storedValue = await table.load(0, textEncoder.encode(key));
|
||||||
expect(storedValue).toBeDefined();
|
expect(storedValue).toBeDefined();
|
||||||
expect(unmarshall(storedValue!)).toBe(value);
|
expect(textDecoder.decode(storedValue!)).toBe(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have key in list of keys', async () => {
|
it('should have key in list of keys', async () => {
|
||||||
const keys = await table.getKeys(0);
|
const keys = await table.getKeys(0);
|
||||||
const decodedKeys = keys.map(unmarshall);
|
const decodedKeys = keys.map((key) => textDecoder.decode(key));
|
||||||
expect(decodedKeys).toEqual([key]);
|
expect(decodedKeys).toEqual([key]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -82,15 +86,27 @@ describe('VeilidTable', () => {
|
|||||||
const second = 'second✔';
|
const second = 'second✔';
|
||||||
const third = 'third📢';
|
const third = 'third📢';
|
||||||
|
|
||||||
transaction.store(0, marshall(key), marshall(first));
|
transaction.store(
|
||||||
transaction.store(0, marshall(key), marshall(second));
|
0,
|
||||||
transaction.store(0, marshall(key), marshall(third));
|
textEncoder.encode(key),
|
||||||
|
textEncoder.encode(first)
|
||||||
|
);
|
||||||
|
transaction.store(
|
||||||
|
0,
|
||||||
|
textEncoder.encode(key),
|
||||||
|
textEncoder.encode(second)
|
||||||
|
);
|
||||||
|
transaction.store(
|
||||||
|
0,
|
||||||
|
textEncoder.encode(key),
|
||||||
|
textEncoder.encode(third)
|
||||||
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
|
|
||||||
const storedValue = await table.load(0, marshall(key));
|
const storedValue = await table.load(0, textEncoder.encode(key));
|
||||||
expect(storedValue).toBeDefined();
|
expect(storedValue).toBeDefined();
|
||||||
expect(unmarshall(storedValue!)).toBe(third);
|
expect(textDecoder.decode(storedValue!)).toBe(third);
|
||||||
|
|
||||||
transaction.free();
|
transaction.free();
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,23 @@
|
|||||||
// TextEncoder/TextDecoder are used to solve for "The Unicode Problem" https://stackoverflow.com/a/30106551
|
export const textDecoder = new TextDecoder();
|
||||||
|
export const textEncoder = new TextEncoder();
|
||||||
|
|
||||||
export function marshall(data: string) {
|
// TextEncoder/TextDecoder are used to solve for "The Unicode Problem" https://stackoverflow.com/a/30106551
|
||||||
const byteString = bytesToString(new TextEncoder().encode(data));
|
export function marshallString(data: string) {
|
||||||
|
return marshallBytes(textEncoder.encode(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unmarshallString(b64: string) {
|
||||||
|
return textDecoder.decode(unmarshallBytes(b64));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function marshallBytes(data: Uint8Array) {
|
||||||
|
const byteString = bytesToString(data);
|
||||||
return base64UrlEncode(byteString);
|
return base64UrlEncode(byteString);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unmarshall(b64: string) {
|
export function unmarshallBytes(b64: string) {
|
||||||
const byteString = base64UrlDecode(b64);
|
const byteString = base64UrlDecode(b64);
|
||||||
return new TextDecoder().decode(stringToBytes(byteString));
|
return stringToBytes(byteString);
|
||||||
}
|
}
|
||||||
|
|
||||||
function base64UrlEncode(data: string) {
|
function base64UrlEncode(data: string) {
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
} from './utils/veilid-config';
|
} from './utils/veilid-config';
|
||||||
|
|
||||||
import { veilidClient, veilidCrypto } from 'veilid-wasm';
|
import { veilidClient, veilidCrypto } from 'veilid-wasm';
|
||||||
|
import { textEncoder, unmarshallBytes } from './utils/marshalling-utils';
|
||||||
|
|
||||||
describe('veilidCrypto', () => {
|
describe('veilidCrypto', () => {
|
||||||
before('veilid startup', async () => {
|
before('veilid startup', async () => {
|
||||||
@ -29,10 +30,116 @@ describe('veilidCrypto', () => {
|
|||||||
expect(kinds.includes(bestKind)).toBe(true);
|
expect(kinds.includes(bestKind)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate key pair', async () => {
|
it('should generate key pair', () => {
|
||||||
const bestKind = veilidCrypto.bestCryptoKind();
|
const bestKind = veilidCrypto.bestCryptoKind();
|
||||||
const keypair = veilidCrypto.generateKeyPair(bestKind);
|
const keypair = veilidCrypto.generateKeyPair(bestKind);
|
||||||
expect(typeof keypair).toBe('string');
|
expect(typeof keypair).toBe('string');
|
||||||
// TODO: fix TypeScript return type of generateKeyPair to return string instead of KeyPair
|
|
||||||
|
const [publicKey, secretKey] = keypair.split(':');
|
||||||
|
expect(unmarshallBytes(publicKey).length).toBe(32);
|
||||||
|
expect(unmarshallBytes(secretKey).length).toBe(32);
|
||||||
|
|
||||||
|
const isValid = veilidCrypto.validateKeyPair(
|
||||||
|
bestKind,
|
||||||
|
publicKey,
|
||||||
|
secretKey
|
||||||
|
);
|
||||||
|
expect(isValid).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should generate random bytes', () => {
|
||||||
|
const bestKind = veilidCrypto.bestCryptoKind();
|
||||||
|
const bytes = veilidCrypto.randomBytes(bestKind, 64);
|
||||||
|
expect(bytes instanceof Uint8Array).toBe(true);
|
||||||
|
expect(bytes.length).toBe(64);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hash data and validate hash', () => {
|
||||||
|
const bestKind = veilidCrypto.bestCryptoKind();
|
||||||
|
const data = textEncoder.encode('this is my data🚀');
|
||||||
|
const hash = veilidCrypto.generateHash(bestKind, data);
|
||||||
|
|
||||||
|
expect(hash).toBeDefined();
|
||||||
|
expect(typeof hash).toBe('string');
|
||||||
|
|
||||||
|
const isValid = veilidCrypto.validateHash(bestKind, data, hash);
|
||||||
|
expect(isValid).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hash and validate password', () => {
|
||||||
|
const bestKind = veilidCrypto.bestCryptoKind();
|
||||||
|
|
||||||
|
const password = textEncoder.encode('this is my data🚀');
|
||||||
|
const saltLength = veilidCrypto.defaultSaltLength(bestKind);
|
||||||
|
expect(saltLength).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
const salt = veilidCrypto.randomBytes(bestKind, saltLength);
|
||||||
|
expect(salt instanceof Uint8Array).toBe(true);
|
||||||
|
expect(salt.length).toBe(saltLength);
|
||||||
|
|
||||||
|
const hash = veilidCrypto.hashPassword(bestKind, password, salt);
|
||||||
|
expect(hash).toBeDefined();
|
||||||
|
expect(typeof hash).toBe('string');
|
||||||
|
|
||||||
|
const isValid = veilidCrypto.verifyPassword(bestKind, password, hash);
|
||||||
|
expect(isValid).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should aead encrypt and decrypt', () => {
|
||||||
|
const bestKind = veilidCrypto.bestCryptoKind();
|
||||||
|
const body = textEncoder.encode(
|
||||||
|
'This is an encoded body with my secret data in it🔥'
|
||||||
|
);
|
||||||
|
const ad = textEncoder.encode(
|
||||||
|
'This is data associated with my secret data👋'
|
||||||
|
);
|
||||||
|
|
||||||
|
const nonce = veilidCrypto.randomNonce(bestKind);
|
||||||
|
expect(typeof nonce).toBe('string');
|
||||||
|
|
||||||
|
const sharedSecred = veilidCrypto.randomSharedSecret(bestKind);
|
||||||
|
expect(typeof sharedSecred).toBe('string');
|
||||||
|
|
||||||
|
const encBody = veilidCrypto.encryptAead(
|
||||||
|
bestKind,
|
||||||
|
body,
|
||||||
|
nonce,
|
||||||
|
sharedSecred,
|
||||||
|
ad
|
||||||
|
);
|
||||||
|
expect(encBody instanceof Uint8Array).toBe(true);
|
||||||
|
|
||||||
|
const overhead = veilidCrypto.aeadOverhead(bestKind);
|
||||||
|
expect(encBody.length - body.length).toBe(overhead);
|
||||||
|
|
||||||
|
const decBody = veilidCrypto.decryptAead(
|
||||||
|
bestKind,
|
||||||
|
encBody,
|
||||||
|
nonce,
|
||||||
|
sharedSecred,
|
||||||
|
ad
|
||||||
|
);
|
||||||
|
expect(decBody instanceof Uint8Array).toBe(true);
|
||||||
|
expect(body).toEqual(decBody);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sign and verify', () => {
|
||||||
|
const bestKind = veilidCrypto.bestCryptoKind();
|
||||||
|
const keypair = veilidCrypto.generateKeyPair(bestKind);
|
||||||
|
const data = textEncoder.encode(
|
||||||
|
'This is some data I am signing with my key 🔑'
|
||||||
|
);
|
||||||
|
expect(typeof keypair).toBe('string');
|
||||||
|
|
||||||
|
const [publicKey, secretKey] = keypair.split(':');
|
||||||
|
|
||||||
|
const sig = veilidCrypto.sign(bestKind, publicKey, secretKey, data);
|
||||||
|
expect(typeof sig).toBe('string');
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
const res = veilidCrypto.verify(bestKind, publicKey, data, sig);
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,20 @@ export const config: Options.Testrunner = {
|
|||||||
// Runner Configuration
|
// Runner Configuration
|
||||||
// ====================
|
// ====================
|
||||||
// WebdriverIO supports running e2e tests as well as unit and component tests.
|
// WebdriverIO supports running e2e tests as well as unit and component tests.
|
||||||
runner: ['browser', { viteConfig: './vite.config.ts' }],
|
runner: [
|
||||||
|
'browser',
|
||||||
|
{
|
||||||
|
viteConfig: './vite.config.ts',
|
||||||
|
coverage: {
|
||||||
|
enabled: true,
|
||||||
|
// needed since the ../pkg directory that has the compiled wasm npm package
|
||||||
|
// is outside the current directory. Coverage is only collected on files
|
||||||
|
// that are in within `cwd`.
|
||||||
|
cwd: '..',
|
||||||
|
include: ['pkg/**'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
autoCompileOpts: {
|
autoCompileOpts: {
|
||||||
autoCompile: true,
|
autoCompile: true,
|
||||||
tsNodeOpts: {
|
tsNodeOpts: {
|
||||||
|
Loading…
Reference in New Issue
Block a user