From 848da0ae4ea388f2c9082cb26602934baa72096c Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Fri, 22 Aug 2025 16:53:37 -0400 Subject: [PATCH] Switch to crypto to typed keys everywhere --- CHANGELOG.md | 7 + Cargo.lock | 963 +++++---- Earthfile | 2 +- scripts/clippy_all.sh | 12 +- veilid-core/Cargo.toml | 1 + .../examples/private_route/src/main.rs | 4 +- veilid-core/src/crypto/crypto_system.rs | 203 -- .../{ => crypto_system}/blake3digest512.rs | 0 veilid-core/src/crypto/crypto_system/mod.rs | 213 ++ .../crypto/{ => crypto_system}/none/mod.rs | 216 +- .../crypto/{ => crypto_system}/none/sizes.rs | 0 .../crypto/{ => crypto_system}/vld0/mod.rs | 190 +- .../crypto/{ => crypto_system}/vld0/sizes.rs | 0 .../crypto/{ => crypto_system}/vld1/mod.rs | 2 +- veilid-core/src/crypto/dh_cache.rs | 75 +- veilid-core/src/crypto/envelope.rs | 364 ---- veilid-core/src/crypto/envelope/mod.rs | 497 +++++ veilid-core/src/crypto/guard.rs | 110 +- veilid-core/src/crypto/mod.rs | 100 +- veilid-core/src/crypto/receipt.rs | 238 -- veilid-core/src/crypto/receipt/mod.rs | 337 +++ veilid-core/src/crypto/tests/fixtures.rs | 7 + veilid-core/src/crypto/tests/test_crypto.rs | 10 +- .../src/crypto/tests/test_envelope_receipt.rs | 70 +- veilid-core/src/crypto/tests/test_types.rs | 254 ++- .../src/crypto/types/byte_array_types.rs | 250 +-- veilid-core/src/crypto/types/crypto_typed.rs | 376 ++-- .../src/crypto/types/crypto_typed_group.rs | 567 +++-- veilid-core/src/crypto/types/keypair.rs | 104 +- veilid-core/src/crypto/types/mod.rs | 110 +- .../bootstrap/bootstrap_record.rs | 31 +- .../bootstrap/direct_bootstrap/mod.rs | 2 +- .../bootstrap/direct_bootstrap/v1.rs | 4 +- .../bootstrap/txt_bootstrap/v0.rs | 5 +- .../bootstrap/txt_bootstrap/v1.rs | 5 +- veilid-core/src/network_manager/mod.rs | 105 +- .../src/network_manager/native/igd_manager.rs | 1 + .../src/network_manager/receipt_manager.rs | 9 +- veilid-core/src/network_manager/send_data.rs | 16 +- .../tests/test_signed_node_info.rs | 6 +- .../network_manager/types/dial_info_filter.rs | 2 +- veilid-core/src/network_manager/types/flow.rs | 7 +- .../network_manager/types/protocol_type.rs | 26 +- .../src/network_manager/types/signal_info.rs | 8 +- veilid-core/src/routing_table/bucket.rs | 2 +- veilid-core/src/routing_table/bucket_entry.rs | 163 +- veilid-core/src/routing_table/debug.rs | 2 +- veilid-core/src/routing_table/find_peers.rs | 52 +- veilid-core/src/routing_table/mod.rs | 53 +- .../routing_table/node_ref/node_ref_lock.rs | 1 - .../src/routing_table/node_ref/traits.rs | 20 +- veilid-core/src/routing_table/privacy.rs | 4 +- .../src/routing_table/route_spec_store/mod.rs | 259 ++- .../route_spec_store/permutation.rs | 2 +- .../route_spec_store/route_set_spec_detail.rs | 81 +- .../route_spec_store_cache.rs | 42 +- .../route_spec_store_content.rs | 7 +- .../routing_table/routing_table_inner/mod.rs | 101 +- .../routing_domains/mod.rs | 1 - .../routing_domains/public_internet/mod.rs | 10 +- .../routing_domains/relay_status.rs | 18 +- .../routing_domains/relay_status.rs.orig | 318 --- .../tasks/closest_peers_refresh.rs | 2 +- .../src/routing_table/tasks/kick_buckets.rs | 4 +- .../src/routing_table/tasks/ping_validator.rs | 12 +- .../tasks/private_route_management.rs | 5 +- .../routing_table/tasks/relay_management.rs | 4 +- .../src/routing_table/tests/fixtures.rs | 6 +- .../routing_table/types/hash_coordinate.rs | 60 + veilid-core/src/routing_table/types/mod.rs | 2 + .../src/routing_table/types/node_info.rs | 16 +- .../src/routing_table/types/peer_info.rs | 2 +- .../src/routing_table/types/relay_info.rs | 9 +- .../routing_table/types/veilid_capability.rs | 16 +- veilid-core/src/rpc_processor/answer.rs | 4 +- .../rpc_processor/coders/byte_array_types.rs | 4 +- .../src/rpc_processor/coders/node_info.rs | 3 +- .../coders/operations/operation_find_node.rs | 6 +- .../operations/operation_return_receipt.rs | 6 +- .../coders/operations/operation_route.rs | 6 +- .../operation_validate_dial_info.rs | 6 +- .../operations/operation_watch_value.rs | 23 +- veilid-core/src/rpc_processor/destination.rs | 10 +- veilid-core/src/rpc_processor/error.rs | 1 - .../src/rpc_processor/fanout/fanout_call.rs | 31 +- .../src/rpc_processor/message_header.rs | 6 +- veilid-core/src/rpc_processor/mod.rs | 48 +- .../src/rpc_processor/rendered_operation.rs | 6 +- .../src/rpc_processor/rpc_find_node.rs | 2 +- .../src/rpc_processor/rpc_get_value.rs | 9 +- .../src/rpc_processor/rpc_inspect_value.rs | 9 +- veilid-core/src/rpc_processor/rpc_route.rs | 58 +- .../src/rpc_processor/rpc_set_value.rs | 9 +- .../rpc_processor/rpc_validate_dial_info.rs | 1 + .../src/rpc_processor/rpc_value_changed.rs | 8 +- .../src/rpc_processor/rpc_watch_value.rs | 9 +- veilid-core/src/rpc_processor/rpc_worker.rs | 6 +- veilid-core/src/storage_manager/get_value.rs | 9 +- .../src/storage_manager/inspect_value.rs | 2 +- veilid-core/src/storage_manager/mod.rs | 52 +- .../outbound_watch_state.rs | 4 +- .../outbound_watch_manager/per_node_state.rs | 2 +- .../record_store/local_record_detail.rs | 2 +- veilid-core/src/storage_manager/rehydrate.rs | 7 - veilid-core/src/storage_manager/schema.rs | 4 +- veilid-core/src/storage_manager/set_value.rs | 9 +- .../tasks/offline_subkey_writes.rs | 4 - .../types/signed_value_data.rs | 17 +- .../types/signed_value_descriptor.rs | 13 +- .../src/storage_manager/watch_value.rs | 11 +- veilid-core/src/table_store/mod.rs | 6 +- veilid-core/src/table_store/table_db.rs | 8 +- .../src/table_store/tests/test_table_store.rs | 6 +- veilid-core/src/tests/common/test_dht.rs | 36 +- veilid-core/src/veilid_api/api.rs | 6 +- veilid-core/src/veilid_api/debug.rs | 4 +- veilid-core/src/veilid_api/routing_context.rs | 5 +- .../serialize_untyped_vld0.rs | 2 +- .../src/veilid_api/tests/test_types_dht.rs | 2 +- .../src/veilid_api/types/aligned_u64.rs | 6 +- .../types/dht/dht_record_descriptor.rs | 78 +- .../src/veilid_api/types/dht/schema/mod.rs | 2 +- .../types/dht/set_dht_value_options.rs | 43 +- .../src/veilid_api/types/dht/value_data.rs | 57 +- veilid-core/src/veilid_api/types/fourcc.rs | 144 +- veilid-core/src/veilid_api/types/mod.rs | 4 + .../src/veilid_api/types/route_blob.rs | 28 + veilid-core/src/veilid_api/types/safety.rs | 55 + .../src/veilid_api/types/wasm_helpers.rs | 56 + veilid-core/src/veilid_config.rs | 132 +- .../example/integration_test/app_test.dart | 4 +- .../example/integration_test/test_crypto.dart | 117 +- .../example/integration_test/test_dht.dart | 581 ++--- .../test_routing_context.dart | 6 +- veilid-flutter/lib/routing_context.dart | 35 +- .../lib/routing_context.freezed.dart | 25 +- veilid-flutter/lib/routing_context.g.dart | 6 +- veilid-flutter/lib/veilid.dart | 1 - veilid-flutter/lib/veilid_config.dart | 4 +- veilid-flutter/lib/veilid_config.freezed.dart | 98 +- veilid-flutter/lib/veilid_config.g.dart | 8 +- veilid-flutter/lib/veilid_crypto.dart | 116 +- veilid-flutter/lib/veilid_encoding.dart | 20 +- veilid-flutter/lib/veilid_ffi.dart | 107 +- veilid-flutter/lib/veilid_js.dart | 113 +- veilid-flutter/rust/src/dart_ffi.rs | 113 +- veilid-python/poetry_install.sh | 4 - veilid-python/tests/test_crypto.py | 110 +- veilid-python/tests/test_dht.py | 1118 +++++----- veilid-python/tests/test_routing_context.py | 30 +- veilid-python/veilid/api.py | 72 +- veilid-python/veilid/json_api.py | 182 +- veilid-python/veilid/operations.py | 2 +- veilid-python/veilid/schema/RecvMessage.json | 125 +- veilid-python/veilid/schema/Request.json | 50 +- veilid-python/veilid/types.py | 50 +- veilid-remote-api/src/crypto_system.rs | 84 +- veilid-remote-api/src/lib.rs | 19 +- veilid-remote-api/src/process.rs | 45 +- veilid-remote-api/src/routing_context.rs | 4 +- veilid-wasm/Cargo.toml | 15 +- veilid-wasm/src/api_result.rs | 2 + veilid-wasm/src/dart/mod.rs | 1740 +++++++++++++++ veilid-wasm/src/js/mod.rs | 28 + veilid-wasm/src/{ => js}/veilid_client_js.rs | 103 +- veilid-wasm/src/js/veilid_crypto_js.rs | 331 +++ .../src/{ => js}/veilid_routing_context_js.rs | 100 +- .../src/{ => js}/veilid_table_db_js.rs | 0 veilid-wasm/src/lib.rs | 1917 +---------------- veilid-wasm/src/veilid_crypto_js.rs | 746 ------- veilid-wasm/src/veilid_version.rs | 10 + veilid-wasm/src/veilid_wasm_config.rs | 36 + veilid-wasm/src/wasm_helpers.rs | 64 +- .../tests/src/VeilidRoutingContext.test.ts | 491 ++--- veilid-wasm/tests/src/utils/veilid-config.ts | 14 +- veilid-wasm/tests/src/utils/wait-utils.ts | 4 +- veilid-wasm/tests/src/veilidClient.test.ts | 49 +- veilid-wasm/tests/src/veilidCrypto.test.ts | 247 +-- .../{wasm_build.sh => wasm_build_dart.sh} | 4 +- veilid-wasm/{wasm_test.sh => wasm_test_js.sh} | 0 180 files changed, 8532 insertions(+), 8488 deletions(-) delete mode 100644 veilid-core/src/crypto/crypto_system.rs rename veilid-core/src/crypto/{ => crypto_system}/blake3digest512.rs (100%) create mode 100644 veilid-core/src/crypto/crypto_system/mod.rs rename veilid-core/src/crypto/{ => crypto_system}/none/mod.rs (63%) rename veilid-core/src/crypto/{ => crypto_system}/none/sizes.rs (100%) rename veilid-core/src/crypto/{ => crypto_system}/vld0/mod.rs (76%) rename veilid-core/src/crypto/{ => crypto_system}/vld0/sizes.rs (100%) rename veilid-core/src/crypto/{ => crypto_system}/vld1/mod.rs (55%) delete mode 100644 veilid-core/src/crypto/envelope.rs create mode 100644 veilid-core/src/crypto/envelope/mod.rs delete mode 100644 veilid-core/src/crypto/receipt.rs create mode 100644 veilid-core/src/crypto/receipt/mod.rs delete mode 100644 veilid-core/src/routing_table/routing_table_inner/routing_domains/relay_status.rs.orig create mode 100644 veilid-core/src/routing_table/types/hash_coordinate.rs create mode 100644 veilid-core/src/veilid_api/types/route_blob.rs create mode 100644 veilid-core/src/veilid_api/types/wasm_helpers.rs delete mode 100755 veilid-python/poetry_install.sh create mode 100644 veilid-wasm/src/api_result.rs create mode 100644 veilid-wasm/src/dart/mod.rs create mode 100644 veilid-wasm/src/js/mod.rs rename veilid-wasm/src/{ => js}/veilid_client_js.rs (74%) create mode 100644 veilid-wasm/src/js/veilid_crypto_js.rs rename veilid-wasm/src/{ => js}/veilid_routing_context_js.rs (86%) rename veilid-wasm/src/{ => js}/veilid_table_db_js.rs (100%) delete mode 100644 veilid-wasm/src/veilid_crypto_js.rs create mode 100644 veilid-wasm/src/veilid_version.rs create mode 100644 veilid-wasm/src/veilid_wasm_config.rs rename veilid-wasm/{wasm_build.sh => wasm_build_dart.sh} (91%) rename veilid-wasm/{wasm_test.sh => wasm_test_js.sh} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 071f51d7..894d15dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ - This will only be a breaking change for anyone utilizing the previous `writer` argument. - `writer` is now a member of `SetDHTValueOptions`, alongside the new `allow_offline` property. - Eliminated DHTW capability, merged into DHTV capability, now there is only one DHT enabling/disabling capability and all operations are part of it + - Crypto / CryptoSystem functions now use typed keys everywhere (#483) + - Eliminated 'best' CryptoKind concept, crypto kinds must now be explicitly stated, otherwise upgrades of veilid-core that change the 'best' CryptoKind could break functionality silently. - veilid-core: - Add private route example @@ -29,6 +31,7 @@ - Improved `TypedXXX` conversion traits, including to and from `Vec` - Ensure utf8 replacement characters are never emitted in logs - Export `CRYPTO_KIND_VLD0` constant + - Added SequenceOrdering enum to represent ordering mode for protocols rather than a bool - veilid-python: - Correction of type hints @@ -38,6 +41,10 @@ - veilid-server: - Use `detect_address_changes: auto` by default +- veilid-wasm: + - Reorganize crate and add `js` and `dart` features to enable different target bindings + - Revamp bindings for `js` to eliminate excessive strings and improve marshaling + **Changed in Veilid 0.4.7** - _BREAKING API CHANGES_: diff --git a/Cargo.lock b/Cargo.lock index 0a6d6e13..b78decb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -33,7 +33,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "cipher 0.3.0", "cpufeatures", "opaque-debug", @@ -45,7 +45,7 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "getrandom 0.3.3", "once_cell", "version_check", @@ -130,12 +130,21 @@ dependencies = [ [[package]] name = "ansi-parser" version = "0.9.1" -source = "git+https://gitlab.com/davidbittner/ansi-parser.git#6b973008e08069d95316790ab73a5847e600c702" +source = "git+https://gitlab.com/davidbittner/ansi-parser.git#17ae818715ccdd3d34c0d00231eecce2b170e539" dependencies = [ "heapless 0.8.0", "nom 8.0.0", ] +[[package]] +name = "ansi-width" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219e3ce6f2611d83b51ec2098a12702112c29e57203a6b0a0929b2cddb486608" +dependencies = [ + "unicode-width 0.1.14", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -147,9 +156,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -162,50 +171,50 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "arboard" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" +checksum = "55f533f8e0af236ffe5eb979b99381df3258853f00ba2e44b6e1955292c75227" dependencies = [ "clipboard-win", "log", @@ -270,9 +279,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -289,7 +298,7 @@ dependencies = [ "async-task", "concurrent-queue", "fastrand 2.3.0", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "pin-project-lite", "slab", ] @@ -300,12 +309,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", - "async-io 2.4.1", - "async-lock 3.4.0", + "async-io 2.5.0", + "async-lock 3.4.1", "blocking", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "once_cell", ] @@ -317,7 +326,7 @@ checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock 2.8.0", "autocfg", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "concurrent-queue", "futures-lite 1.13.0", "log", @@ -331,21 +340,20 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ - "async-lock 3.4.0", - "cfg-if 1.0.0", + "async-lock 3.4.1", + "cfg-if 1.0.3", "concurrent-queue", "futures-io", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "parking", - "polling 3.8.0", - "rustix 1.0.7", + "polling 3.10.0", + "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -359,69 +367,68 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-process" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ - "async-channel 2.3.1", - "async-io 2.4.1", - "async-lock 3.4.0", + "async-channel 2.5.0", + "async-io 2.5.0", + "async-lock 3.4.1", "async-signal", "async-task", "blocking", - "cfg-if 1.0.0", - "event-listener 5.4.0", - "futures-lite 2.6.0", - "rustix 1.0.7", - "tracing", + "cfg-if 1.0.3", + "event-listener 5.4.1", + "futures-lite 2.6.1", + "rustix 1.0.8", ] [[package]] name = "async-signal" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ - "async-io 2.4.1", - "async-lock 3.4.0", + "async-io 2.5.0", + "async-lock 3.4.1", "atomic-waker", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "futures-core", "futures-io", - "rustix 1.0.7", + "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "async-std" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-attributes", "async-channel 1.9.0", "async-global-executor", - "async-io 2.4.1", - "async-lock 3.4.0", + "async-io 2.5.0", + "async-lock 3.4.1", "async-process", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "gloo-timers 0.3.0", "kv-log-macro", "log", @@ -476,7 +483,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -500,13 +507,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -626,9 +633,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" @@ -682,7 +689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", "miniz_oxide", "object", @@ -739,9 +746,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "bitmaps" @@ -767,7 +774,7 @@ dependencies = [ "arrayref", "arrayvec", "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "constant_time_eq", ] @@ -779,7 +786,7 @@ checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -818,14 +825,14 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", - "futures-lite 2.6.0", + "futures-lite 2.6.1", "piper", ] @@ -849,9 +856,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -876,27 +883,27 @@ dependencies = [ [[package]] name = "capnp" -version = "0.21.1" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b3560604b407fe0ab6f89b81ac11887b4ae07f8d31486581dee5b353e48f06" +checksum = "def25bdbbc2758b363d79129c7f277520e3347e8b647c404d4823591f837c4ad" dependencies = [ "embedded-io 0.6.1", ] [[package]] name = "capnpc" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5af589f7a7f3e6d920120b913345bd9a2fc65dfd76c5053a142852a5ea2e8609" +checksum = "d93a18ec8176d4a87f1852b6a560b4196729365c01ba3cad03b73a376a23c56e" dependencies = [ "capnp", ] [[package]] name = "cc" -version = "1.2.25" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" dependencies = [ "shlex", ] @@ -924,9 +931,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -940,7 +947,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "cipher 0.4.4", "cpufeatures", ] @@ -1021,9 +1028,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -1031,9 +1038,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -1044,27 +1051,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clipboard-win" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" dependencies = [ "error-code", ] @@ -1080,9 +1087,12 @@ dependencies = [ [[package]] name = "cobs" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.15", +] [[package]] name = "color-eyre" @@ -1099,9 +1109,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "combine" @@ -1146,7 +1156,19 @@ dependencies = [ "nom 7.1.3", "pathdiff", "serde", - "yaml-rust2", + "yaml-rust2 0.8.1", +] + +[[package]] +name = "config" +version = "0.15.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4092bf3922a966e2bd74640b80f36c73eaa7251a4fd0fbcda1f8a4de401352" +dependencies = [ + "pathdiff", + "serde", + "winnow 0.7.12", + "yaml-rust2 0.10.3", ] [[package]] @@ -1158,7 +1180,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.0", + "unicode-width 0.2.1", "windows-sys 0.59.0", ] @@ -1206,7 +1228,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "wasm-bindgen", ] @@ -1258,11 +1280,11 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", ] [[package]] @@ -1292,7 +1314,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "crossterm_winapi", "libc", "mio 0.8.11", @@ -1308,14 +1330,14 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "crossterm_winapi", "derive_more", "document-features", "futures-core", "mio 1.0.4", "parking_lot 0.12.4", - "rustix 1.0.7", + "rustix 1.0.8", "signal-hook", "signal-hook-mio", "winapi", @@ -1358,7 +1380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -1378,7 +1400,7 @@ source = "git+https://gitlab.com/veilid/cursive.git#b518d61e61d75a70788b0f7f371c dependencies = [ "ahash", "async-std", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "crossbeam-channel", "crossterm 0.27.0", "cursive_core", @@ -1409,7 +1431,7 @@ dependencies = [ "log", "smallvec", "unicode-segmentation", - "unicode-width 0.2.0", + "unicode-width 0.2.1", ] [[package]] @@ -1433,7 +1455,7 @@ dependencies = [ "serde_yaml", "time", "tokio", - "toml 0.8.22", + "toml 0.8.23", "unicode-segmentation", "unicode-width 0.1.14", "xi-unicode", @@ -1453,7 +1475,7 @@ version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", @@ -1471,7 +1493,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -1503,6 +1525,16 @@ dependencies = [ "darling_macro 0.20.11", ] +[[package]] +name = "darling" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08440b3dd222c3d0433e63e097463969485f112baff337dfdaca043a0d760570" +dependencies = [ + "darling_core 0.21.2", + "darling_macro 0.21.2", +] + [[package]] name = "darling_core" version = "0.13.4" @@ -1528,7 +1560,20 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.101", + "syn 2.0.106", +] + +[[package]] +name = "darling_core" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25b7912bc28a04ab1b7715a68ea03aaa15662b43a1a4b2c480531fd19f8bf7e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -1550,7 +1595,18 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.101", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce154b9bea7fb0c8e8326e62d00354000c36e79770ff21b8c84e3aa267d9d531" +dependencies = [ + "darling_core 0.21.2", + "quote", + "syn 2.0.106", ] [[package]] @@ -1559,7 +1615,7 @@ version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "hashbrown 0.14.5", "lock_api", "once_cell", @@ -1621,7 +1677,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -1671,7 +1727,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "objc2", ] @@ -1683,7 +1739,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -1697,9 +1753,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ed25519" @@ -1713,9 +1769,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", @@ -1757,7 +1813,7 @@ version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", ] [[package]] @@ -1769,7 +1825,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -1789,7 +1845,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -1815,9 +1871,9 @@ dependencies = [ [[package]] name = "enumset" -version = "1.1.6" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6b7c3d347de0a9f7bfd2f853be43fe32fa6fac30c70f6d6d67a1e936b87ee" +checksum = "f50acec76c668b4621fe3694e5ddee53c8fae2a03410026f50947d195eb44dc1" dependencies = [ "enumset_derive", "serde", @@ -1825,14 +1881,14 @@ dependencies = [ [[package]] name = "enumset_derive" -version = "0.11.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da3ea9e1d1a3b1593e15781f930120e72aa7501610b2f82e5b6739c72e8eac5" +checksum = "588eaef9dbc5d72c5fa4edf162527e67dab5d14ba61519ef7c8e233d5bdc092c" dependencies = [ - "darling 0.20.11", + "darling 0.21.2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -1866,12 +1922,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1888,9 +1944,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -1905,7 +1961,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "pin-project-lite", ] @@ -1968,7 +2024,7 @@ version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", "libredox", "windows-sys 0.59.0", @@ -1976,9 +2032,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -2125,9 +2181,9 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand 2.3.0", "futures-core", @@ -2144,7 +2200,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -2231,10 +2287,10 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -2244,7 +2300,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", @@ -2258,9 +2314,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gloo-timers" @@ -2332,9 +2388,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ "bytes 1.10.1", "fnv", @@ -2342,7 +2398,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -2400,9 +2456,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] [[package]] name = "hashlink" @@ -2413,6 +2472,15 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "hdrhistogram" version = "7.5.4" @@ -2473,9 +2541,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2490,7 +2558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" dependencies = [ "async-trait", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "data-encoding", "enum-as-inner", "futures-channel", @@ -2513,7 +2581,7 @@ version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "futures-util", "hickory-proto", "ipconfig", @@ -2568,6 +2636,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "hostname" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +dependencies = [ + "cfg-if 1.0.3", + "libc", + "windows-link", +] + [[package]] name = "http" version = "0.2.12" @@ -2846,9 +2925,9 @@ checksum = "d9f1a0777d972970f204fdf8ef319f1f4f8459131636d7e3c96c5d59570d0fa6" [[package]] name = "indenter" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" @@ -2863,12 +2942,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.5", "serde", ] @@ -2887,7 +2966,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", ] [[package]] @@ -2901,6 +2980,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.2", + "cfg-if 1.0.3", + "libc", +] + [[package]] name = "ipconfig" version = "0.3.2" @@ -2968,7 +3058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "combine", "jni-sys", "log", @@ -3006,7 +3096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aed4aad1a5c0ae5cfd990fd8cb7e1e31d1742e04c964a62b6928962838c766d" dependencies = [ "byteorder", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "core-foundation", "core-foundation-sys", "directories", @@ -3097,9 +3187,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libc-print" @@ -3116,19 +3206,19 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ - "cfg-if 1.0.0", - "windows-targets 0.53.0", + "cfg-if 1.0.3", + "windows-targets 0.53.3", ] [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.17", ] [[package]] @@ -3186,9 +3276,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "lock_api" @@ -3229,9 +3319,9 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" [[package]] name = "match_cfg" @@ -3268,9 +3358,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" @@ -3305,9 +3395,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] @@ -3320,7 +3410,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -3332,7 +3422,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -3488,7 +3578,7 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.12", + "thiserror 2.0.15", ] [[package]] @@ -3513,7 +3603,7 @@ checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" dependencies = [ "bitflags 1.3.2", "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", "memoffset", ] @@ -3525,7 +3615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", ] @@ -3535,8 +3625,8 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.9.1", - "cfg-if 1.0.0", + "bitflags 2.9.2", + "cfg-if 1.0.3", "libc", ] @@ -3546,8 +3636,8 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.1", - "cfg-if 1.0.0", + "bitflags 2.9.2", + "cfg-if 1.0.3", "cfg_aliases", "libc", ] @@ -3558,8 +3648,8 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", - "cfg-if 1.0.0", + "bitflags 2.9.2", + "cfg-if 1.0.3", "cfg_aliases", "libc", ] @@ -3713,9 +3803,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" dependencies = [ "objc2-encode", ] @@ -3726,7 +3816,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "objc2", "objc2-core-graphics", "objc2-foundation", @@ -3738,7 +3828,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "dispatch2", "objc2", ] @@ -3749,7 +3839,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "dispatch2", "objc2", "objc2-core-foundation", @@ -3768,7 +3858,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "objc2", "objc2-core-foundation", ] @@ -3779,7 +3869,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "objc2", "objc2-core-foundation", ] @@ -3817,8 +3907,8 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", - "cfg-if 1.0.0", + "bitflags 2.9.2", + "cfg-if 1.0.3", "foreign-types", "libc", "once_cell", @@ -3834,7 +3924,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -4075,9 +4165,9 @@ dependencies = [ [[package]] name = "owo-colors" -version = "4.2.1" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" [[package]] name = "paranoid-android" @@ -4126,7 +4216,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "instant", "libc", "redox_syscall 0.2.16", @@ -4140,9 +4230,9 @@ version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.17", "smallvec", "windows-targets 0.52.6", ] @@ -4229,7 +4319,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -4279,7 +4369,7 @@ checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags 1.3.2", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "concurrent-queue", "libc", "log", @@ -4289,17 +4379,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.8.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "concurrent-queue", - "hermit-abi 0.5.1", + "hermit-abi 0.5.2", "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -4315,9 +4404,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -4330,9 +4419,9 @@ dependencies = [ [[package]] name = "postcard" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -4403,14 +4492,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -4458,7 +4547,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -4487,9 +4576,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -4559,11 +4648,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -4594,7 +4683,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -4694,7 +4783,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "getrandom 0.2.16", "libc", "untrusted", @@ -4747,7 +4836,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -4757,9 +4846,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -4796,7 +4885,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4805,15 +4894,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4849,23 +4938,23 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rustyline-async" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055f3061e6c11d3c981f55b9c60da44d0413344ceeaf2fb479450f18dc9c2675" +checksum = "6e07ddce8399c61495b405dc94d4f30d01fc1c5e1238f10b9c09940678bc81ab" dependencies = [ + "ansi-width", "crossterm 0.29.0", "futures-util", "pin-project 1.1.10", "thingbuf", - "thiserror 2.0.12", + "thiserror 2.0.15", "unicode-segmentation", - "unicode-width 0.2.0", ] [[package]] @@ -4895,9 +4984,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" dependencies = [ "sdd", ] @@ -4913,9 +5002,21 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", "ref-cast", @@ -4926,14 +5027,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b13ed22d6d49fe23712e068770b5c4df4a693a2b02eeff8e7ca3135627a24f6" +checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -4960,9 +5061,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" [[package]] name = "secret-service" @@ -4990,7 +5091,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "core-foundation", "core-foundation-sys", "libc", @@ -5084,7 +5185,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -5095,14 +5196,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -5118,14 +5219,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -5153,15 +5254,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", + "indexmap 2.10.0", + "schemars 0.9.0", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -5171,14 +5274,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -5187,7 +5290,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -5200,7 +5303,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4db627b98b36d4203a7b458cf3573730f2bb591b28871d916dfa9efabfd41f" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -5243,7 +5346,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -5254,7 +5357,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -5263,7 +5366,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "cpufeatures", "digest 0.10.7", ] @@ -5275,7 +5378,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -5287,7 +5390,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "cpufeatures", "digest 0.10.7", ] @@ -5349,9 +5452,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -5378,18 +5481,15 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "snailquote" @@ -5421,6 +5521,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -5459,7 +5569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" dependencies = [ "async-channel 1.9.0", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "futures-core", "pin-project-lite", ] @@ -5501,9 +5611,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -5524,7 +5634,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -5533,7 +5643,7 @@ version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "core-foundation-sys", "libc", "ntapi", @@ -5564,15 +5674,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand 2.3.0", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -5586,12 +5696,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -5624,11 +5734,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.15", ] [[package]] @@ -5639,18 +5749,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -5665,12 +5775,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "cfg-if 1.0.0", - "once_cell", + "cfg-if 1.0.3", ] [[package]] @@ -5718,9 +5827,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -5733,28 +5842,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes 1.10.1", + "io-uring", "libc", "mio 1.0.4", "parking_lot 0.12.4", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.10", + "slab", + "socket2 0.6.0", "tokio-macros", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "tokio-io-timeout" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +checksum = "0bd86198d9ee903fedd2f9a2e72014287c0d9167e4ae43b5853007205dda1b76" dependencies = [ "pin-project-lite", "tokio", @@ -5768,7 +5879,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -5794,9 +5905,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes 1.10.1", "futures-core", @@ -5817,21 +5928,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.26", + "toml_edit 0.22.27", ] [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] @@ -5842,30 +5953,30 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", "toml_write", - "winnow 0.7.10", + "winnow 0.7.12", ] [[package]] name = "toml_write" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "tonic" @@ -5980,20 +6091,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -6095,7 +6206,7 @@ checksum = "9bc58223383423483e4bc056c7e7b3f77bdee924a9d33834112c69ead06dc847" dependencies = [ "bindgen", "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "fnv", "once_cell", "parking_lot 0.11.2", @@ -6177,7 +6288,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -6261,9 +6372,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode_categories" @@ -6355,7 +6466,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -6398,10 +6509,10 @@ dependencies = [ "arboard", "async-std", "async-tungstenite 0.23.0", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "chrono", - "clap 4.5.39", - "config 0.13.4", + "clap 4.5.45", + "config 0.15.14", "console", "crossbeam-channel", "cursive", @@ -6448,7 +6559,7 @@ dependencies = [ "bytes 1.10.1", "capnp", "capnpc", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "chacha20", "chacha20poly1305", "chrono", @@ -6495,7 +6606,7 @@ dependencies = [ "rustls", "rustls-pemfile", "sanitize-filename", - "schemars", + "schemars 1.0.4", "send_wrapper 0.6.0", "serde", "serde-big-array", @@ -6525,6 +6636,7 @@ dependencies = [ "veilid-tools", "veilid-tracing-wasm", "wasm-bindgen", + "wasm-bindgen-derive", "wasm-bindgen-futures", "wasm-bindgen-test", "wasm-logger", @@ -6552,7 +6664,7 @@ name = "veilid-core-examples-private-route" version = "0.1.0" dependencies = [ "async-stdin", - "clap 4.5.39", + "clap 4.5.45", "ctrlc", "data-encoding", "tokio", @@ -6569,7 +6681,7 @@ dependencies = [ "android_log-sys 0.3.2", "async-std", "backtrace", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "ctor", "data-encoding", "ffi-support", @@ -6624,7 +6736,7 @@ name = "veilid-remote-api" version = "0.4.8" dependencies = [ "parking_lot 0.12.4", - "schemars", + "schemars 1.0.4", "serde", "serde_json", "tracing", @@ -6639,9 +6751,9 @@ dependencies = [ "async-std", "async-tungstenite 0.27.0", "backtrace", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "chrono", - "clap 4.5.39", + "clap 4.5.45", "color-eyre", "config 0.14.1", "console-subscriber", @@ -6694,14 +6806,14 @@ version = "0.4.8" dependencies = [ "android_logger 0.13.3", "async-io 1.13.0", - "async-lock 3.4.0", + "async-lock 3.4.1", "async-std", "async-tungstenite 0.28.2", "async_executors", "backtrace", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "chrono", - "clap 4.5.39", + "clap 4.5.45", "console_error_panic_hook", "ctrlc", "eyre", @@ -6778,7 +6890,7 @@ dependencies = [ name = "veilid-wasm" version = "0.4.8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "console_error_panic_hook", "data-encoding", "futures-util", @@ -6786,7 +6898,8 @@ dependencies = [ "js-sys", "lazy_static", "parking_lot 0.12.4", - "send_wrapper 0.4.0", + "paste", + "send_wrapper 0.6.0", "serde", "serde-wasm-bindgen", "serde_bytes", @@ -6797,6 +6910,7 @@ dependencies = [ "veilid-core", "veilid-tracing-wasm", "wasm-bindgen", + "wasm-bindgen-derive", "wasm-bindgen-futures", "wasm-bindgen-test", ] @@ -6834,9 +6948,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -6853,7 +6967,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "once_cell", "rustversion", "serde", @@ -6871,17 +6985,39 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ab8fdb87408dab27d43267c311c89135b35d13f8b3081e88451ddeff742c93a" +dependencies = [ + "js-sys", + "wasm-bindgen", + "wasm-bindgen-derive-macro", +] + +[[package]] +name = "wasm-bindgen-derive-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbc080f15cb38f447d52bbae64630c2d4925a9ecb5d140d56c0910b69b4cc7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "wasm-bindgen-futures" version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "js-sys", "once_cell", "wasm-bindgen", @@ -6906,7 +7042,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6941,7 +7077,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -7012,7 +7148,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7aafc5e81e847f05d6770e074faf7b1cd4a5dec9a0e88eac5d55e20fdfebee9a" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-core", "parking_lot 0.12.4", "pin-project-lite", @@ -7055,11 +7191,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -7127,7 +7263,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -7138,14 +7274,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-permissions" @@ -7172,7 +7308,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "193cae8e647981c35bc947fdd57ba7928b1fa0d4a79305f6dd2dc55221ac35ac" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "widestring", "windows-sys 0.59.0", ] @@ -7222,6 +7358,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -7270,10 +7415,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -7475,9 +7621,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -7488,7 +7634,7 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "windows-sys 0.48.0", ] @@ -7498,7 +7644,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] @@ -7515,7 +7661,7 @@ checksum = "ed39ff9f8b2eda91bf6390f9f49eee93d655489e15708e3bb638c1c4f07cecb4" dependencies = [ "async-tungstenite 0.28.2", "async_io_stream", - "bitflags 2.9.1", + "bitflags 2.9.2", "futures-core", "futures-io", "futures-sink", @@ -7528,9 +7674,9 @@ dependencies = [ [[package]] name = "ws_stream_wasm" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" dependencies = [ "async_io_stream", "futures", @@ -7539,7 +7685,7 @@ dependencies = [ "pharos", "rustc_version", "send_wrapper 0.6.0", - "thiserror 1.0.69", + "thiserror 2.0.15", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -7582,9 +7728,9 @@ checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "xmltree" @@ -7612,7 +7758,18 @@ checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8" dependencies = [ "arraydeque", "encoding_rs", - "hashlink", + "hashlink 0.8.4", +] + +[[package]] +name = "yaml-rust2" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink 0.10.0", ] [[package]] @@ -7635,7 +7792,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", "synstructure", ] @@ -7676,22 +7833,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -7711,7 +7868,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", "synstructure", ] @@ -7732,7 +7889,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] @@ -7748,9 +7905,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -7765,7 +7922,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.106", ] [[package]] diff --git a/Earthfile b/Earthfile index 256c05b6..ee8a2bcf 100644 --- a/Earthfile +++ b/Earthfile @@ -281,7 +281,7 @@ unit-tests-wasm-linux: FROM +code-linux # Just run build now because actual unit tests require network access # which should be moved to a separate integration test - RUN veilid-wasm/wasm_build.sh release + RUN veilid-wasm/wasm_build_dart.sh release unit-tests-linux: WAIT diff --git a/scripts/clippy_all.sh b/scripts/clippy_all.sh index f0c48e98..77e8f34d 100755 --- a/scripts/clippy_all.sh +++ b/scripts/clippy_all.sh @@ -2,10 +2,10 @@ SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" pushd $SCRIPTDIR/.. >/dev/null -cargo-zigbuild clippy --target x86_64-unknown-linux-gnu -cargo-zigbuild clippy --target x86_64-unknown-linux-gnu --manifest-path=veilid-server/Cargo.toml --no-default-features --features=default-async-std -cargo-zigbuild clippy --target x86_64-pc-windows-gnu -cargo-zigbuild clippy --target aarch64-apple-darwin -cargo clippy --manifest-path=veilid-wasm/Cargo.toml --target wasm32-unknown-unknown +cargo-zigbuild clippy --target x86_64-unknown-linux-gnu $@ +cargo-zigbuild clippy --target x86_64-unknown-linux-gnu --manifest-path=veilid-server/Cargo.toml --no-default-features --features=default-async-std $@ +cargo-zigbuild clippy --target x86_64-pc-windows-gnu $@ +cargo-zigbuild clippy --target aarch64-apple-darwin $@ +cargo clippy --manifest-path=veilid-wasm/Cargo.toml --target wasm32-unknown-unknown $@ -popd >/dev/null \ No newline at end of file +popd >/dev/null diff --git a/veilid-core/Cargo.toml b/veilid-core/Cargo.toml index 1a6fa3ba..a94d0912 100644 --- a/veilid-core/Cargo.toml +++ b/veilid-core/Cargo.toml @@ -233,6 +233,7 @@ serde_bytes = { version = "0.11", default-features = false, features = [ ] } tsify = { version = "0.5.5", features = ["js"] } serde-wasm-bindgen = "0.6.5" +wasm-bindgen-derive = "0.3.0" # Network ws_stream_wasm = "0.7.4" diff --git a/veilid-core/examples/private_route/src/main.rs b/veilid-core/examples/private_route/src/main.rs index c4cac148..ba53566a 100644 --- a/veilid-core/examples/private_route/src/main.rs +++ b/veilid-core/examples/private_route/src/main.rs @@ -184,13 +184,13 @@ async fn create_route( veilid_api_scope(update_callback, config, |veilid_api| async move { // Create a new private route endpoint - let (route_id, route_blob) = + let RouteBlob { route_id, blob } = try_again_loop(|| async { veilid_api.new_private_route().await }).await?; // Print the blob println!( "Route id created: {route_id}\nConnect with this private route blob:\ncargo run --example private-route-example -- --connect {}", - data_encoding::BASE64.encode(&route_blob) + data_encoding::BASE64.encode(&blob) ); // Wait for enter key to exit the application diff --git a/veilid-core/src/crypto/crypto_system.rs b/veilid-core/src/crypto/crypto_system.rs deleted file mode 100644 index 07b945cc..00000000 --- a/veilid-core/src/crypto/crypto_system.rs +++ /dev/null @@ -1,203 +0,0 @@ -use super::*; - -pub(crate) const VEILID_DOMAIN_API: &[u8] = b"VEILID_API"; - -pub trait CryptoSystem { - // Accessors - fn kind(&self) -> CryptoKind; - fn crypto(&self) -> VeilidComponentGuard<'_, Crypto>; - - // Cached Operations - fn cached_dh( - &self, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult; - - // Generation - fn random_bytes(&self, len: u32) -> Vec; - fn hash_password(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult; - fn verify_password(&self, password: &[u8], password_hash: &str) -> VeilidAPIResult; - fn derive_shared_secret( - &self, - password: &[u8], - salt: &[u8], - ) -> VeilidAPIResult; - fn random_nonce(&self) -> BareNonce; - fn random_shared_secret(&self) -> BareSharedSecret; - fn compute_dh( - &self, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult; - fn generate_shared_secret( - &self, - key: &BarePublicKey, - secret: &BareSecretKey, - domain: &[u8], - ) -> VeilidAPIResult { - let dh = self.compute_dh(key, secret)?; - Ok(BareSharedSecret::from( - self.generate_hash(&[&dh, domain, VEILID_DOMAIN_API].concat()), - )) - } - fn generate_keypair(&self) -> BareKeyPair; - fn generate_hash(&self, data: &[u8]) -> BareHashDigest; - fn generate_hash_reader( - &self, - reader: &mut dyn std::io::Read, - ) -> VeilidAPIResult; - - // Validation - fn shared_secret_length(&self) -> usize; - fn nonce_length(&self) -> usize; - fn hash_digest_length(&self) -> usize; - fn public_key_length(&self) -> usize; - fn secret_key_length(&self) -> usize; - fn signature_length(&self) -> usize; - fn default_salt_length(&self) -> usize; - fn aead_overhead(&self) -> usize; - - fn check_shared_secret(&self, secret: &BareSharedSecret) -> VeilidAPIResult<()> { - if secret.len() != self.shared_secret_length() { - apibail_generic!(format!( - "invalid shared secret length: {} != {}", - secret.len(), - self.shared_secret_length() - )); - } - Ok(()) - } - fn check_nonce(&self, nonce: &BareNonce) -> VeilidAPIResult<()> { - if nonce.len() != self.nonce_length() { - apibail_generic!(format!( - "invalid nonce length: {} != {}", - nonce.len(), - self.nonce_length() - )); - } - Ok(()) - } - fn check_hash_digest(&self, hash: &BareHashDigest) -> VeilidAPIResult<()> { - if hash.len() != self.hash_digest_length() { - apibail_generic!(format!( - "invalid hash digest length: {} != {}", - hash.len(), - self.hash_digest_length() - )); - } - Ok(()) - } - fn check_public_key(&self, key: &BarePublicKey) -> VeilidAPIResult<()> { - if key.len() != self.public_key_length() { - apibail_generic!(format!( - "invalid public key length: {} != {}", - key.len(), - self.public_key_length() - )); - } - Ok(()) - } - fn check_secret_key(&self, key: &BareSecretKey) -> VeilidAPIResult<()> { - if key.len() != self.secret_key_length() { - apibail_generic!(format!( - "invalid secret key length: {} != {}", - key.len(), - self.secret_key_length() - )); - } - Ok(()) - } - fn check_signature(&self, signature: &BareSignature) -> VeilidAPIResult<()> { - if signature.len() != self.signature_length() { - apibail_generic!(format!( - "invalid signature length: {} != {}", - signature.len(), - self.signature_length() - )); - } - Ok(()) - } - - fn validate_keypair(&self, key: &BarePublicKey, secret: &BareSecretKey) -> bool; - fn validate_hash(&self, data: &[u8], hash: &BareHashDigest) -> bool; - fn validate_hash_reader( - &self, - reader: &mut dyn std::io::Read, - hash: &BareHashDigest, - ) -> VeilidAPIResult; - - // Distance Metric - fn distance(&self, hash1: &BareHashDigest, hash2: &BareHashDigest) -> BareHashDistance; - - // Authentication - fn sign( - &self, - key: &BarePublicKey, - secret: &BareSecretKey, - data: &[u8], - ) -> VeilidAPIResult; - fn verify( - &self, - key: &BarePublicKey, - data: &[u8], - signature: &BareSignature, - ) -> VeilidAPIResult; - - // AEAD Encrypt/Decrypt - fn decrypt_in_place_aead( - &self, - body: &mut Vec, - nonce: &BareNonce, - shared_secret: &BareSharedSecret, - associated_data: Option<&[u8]>, - ) -> VeilidAPIResult<()>; - fn decrypt_aead( - &self, - body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, - associated_data: Option<&[u8]>, - ) -> VeilidAPIResult>; - fn encrypt_in_place_aead( - &self, - body: &mut Vec, - nonce: &BareNonce, - shared_secret: &BareSharedSecret, - associated_data: Option<&[u8]>, - ) -> VeilidAPIResult<()>; - fn encrypt_aead( - &self, - body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, - associated_data: Option<&[u8]>, - ) -> VeilidAPIResult>; - - // NoAuth Encrypt/Decrypt - fn crypt_in_place_no_auth( - &self, - body: &mut [u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, - ) -> VeilidAPIResult<()>; - fn crypt_b2b_no_auth( - &self, - in_buf: &[u8], - out_buf: &mut [u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, - ) -> VeilidAPIResult<()>; - fn crypt_no_auth_aligned_8( - &self, - body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, - ) -> VeilidAPIResult>; - fn crypt_no_auth_unaligned( - &self, - body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, - ) -> VeilidAPIResult>; -} diff --git a/veilid-core/src/crypto/blake3digest512.rs b/veilid-core/src/crypto/crypto_system/blake3digest512.rs similarity index 100% rename from veilid-core/src/crypto/blake3digest512.rs rename to veilid-core/src/crypto/crypto_system/blake3digest512.rs diff --git a/veilid-core/src/crypto/crypto_system/mod.rs b/veilid-core/src/crypto/crypto_system/mod.rs new file mode 100644 index 00000000..cf204cb6 --- /dev/null +++ b/veilid-core/src/crypto/crypto_system/mod.rs @@ -0,0 +1,213 @@ +use super::*; +mod blake3digest512; + +#[cfg(feature = "enable-crypto-none")] +pub(crate) mod none; +#[cfg(feature = "enable-crypto-vld0")] +pub(crate) mod vld0; +// #[cfg(feature = "enable-crypto-vld1")] +// pub(crate) mod vld1; + +pub(crate) const VEILID_DOMAIN_API: &[u8] = b"VEILID_API"; + +#[cfg(feature = "enable-crypto-none")] +pub use none::sizes::*; +#[cfg(feature = "enable-crypto-none")] +pub use none::*; +#[cfg(feature = "enable-crypto-vld0")] +pub use vld0::sizes::*; +#[cfg(feature = "enable-crypto-vld0")] +pub use vld0::*; +// #[cfg(feature = "enable-crypto-vld1")] +// pub use vld1::*; + +pub use blake3digest512::*; + +pub trait CryptoSystem { + // Accessors + fn kind(&self) -> CryptoKind; + fn crypto(&self) -> VeilidComponentGuard<'_, Crypto>; + + // Cached Operations + fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult; + + // Generation + fn random_bytes(&self, len: u32) -> Vec; + fn hash_password(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult; + fn verify_password(&self, password: &[u8], password_hash: &str) -> VeilidAPIResult; + fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult; + fn random_nonce(&self) -> Nonce; + fn random_shared_secret(&self) -> SharedSecret; + fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult; + fn generate_shared_secret( + &self, + key: &PublicKey, + secret: &SecretKey, + domain: &[u8], + ) -> VeilidAPIResult { + let dh = self.compute_dh(key, secret)?; + let hash = self.generate_hash(&[&dh.into_value(), domain, VEILID_DOMAIN_API].concat()); + Ok(SharedSecret::new( + hash.kind(), + BareSharedSecret::new(&hash.into_value()), + )) + } + fn generate_keypair(&self) -> KeyPair; + fn generate_hash(&self, data: &[u8]) -> HashDigest; + fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult; + + // Validation + fn shared_secret_length(&self) -> usize; + fn nonce_length(&self) -> usize; + fn hash_digest_length(&self) -> usize; + fn public_key_length(&self) -> usize; + fn secret_key_length(&self) -> usize; + fn signature_length(&self) -> usize; + fn default_salt_length(&self) -> usize; + fn aead_overhead(&self) -> usize; + + fn check_shared_secret(&self, secret: &SharedSecret) -> VeilidAPIResult<()> { + if secret.kind() != self.kind() { + apibail_generic!("incorrect shared secret kind"); + } + if secret.value().len() != self.shared_secret_length() { + apibail_generic!(format!( + "invalid shared secret length: {} != {}", + secret.value().len(), + self.shared_secret_length() + )); + } + Ok(()) + } + fn check_nonce(&self, nonce: &Nonce) -> VeilidAPIResult<()> { + if nonce.len() != self.nonce_length() { + apibail_generic!(format!( + "invalid nonce length: {} != {}", + nonce.len(), + self.nonce_length() + )); + } + Ok(()) + } + fn check_hash_digest(&self, hash: &HashDigest) -> VeilidAPIResult<()> { + if hash.kind() != self.kind() { + apibail_generic!("incorrect hash digest kind"); + } + if hash.value().len() != self.hash_digest_length() { + apibail_generic!(format!( + "invalid hash digest length: {} != {}", + hash.value().len(), + self.hash_digest_length() + )); + } + Ok(()) + } + fn check_public_key(&self, key: &PublicKey) -> VeilidAPIResult<()> { + if key.kind() != self.kind() { + apibail_generic!("incorrect public key kind"); + } + if key.value().len() != self.public_key_length() { + apibail_generic!(format!( + "invalid public key length: {} != {}", + key.value().len(), + self.public_key_length() + )); + } + Ok(()) + } + fn check_secret_key(&self, key: &SecretKey) -> VeilidAPIResult<()> { + if key.kind() != self.kind() { + apibail_generic!("incorrect secret key kind"); + } + if key.value().len() != self.secret_key_length() { + apibail_generic!(format!( + "invalid secret key length: {} != {}", + key.value().len(), + self.secret_key_length() + )); + } + Ok(()) + } + fn check_signature(&self, signature: &Signature) -> VeilidAPIResult<()> { + if signature.kind() != self.kind() { + apibail_generic!("incorrect signature kind"); + } + if signature.value().len() != self.signature_length() { + apibail_generic!(format!( + "invalid signature length: {} != {}", + signature.value().len(), + self.signature_length() + )); + } + Ok(()) + } + + fn validate_keypair(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult; + fn validate_hash(&self, data: &[u8], hash: &HashDigest) -> VeilidAPIResult; + fn validate_hash_reader( + &self, + reader: &mut dyn std::io::Read, + hash: &HashDigest, + ) -> VeilidAPIResult; + + // Authentication + fn sign(&self, key: &PublicKey, secret: &SecretKey, data: &[u8]) -> VeilidAPIResult; + fn verify(&self, key: &PublicKey, data: &[u8], signature: &Signature) -> VeilidAPIResult; + + // AEAD Encrypt/Decrypt + fn decrypt_in_place_aead( + &self, + body: &mut Vec, + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> VeilidAPIResult<()>; + fn decrypt_aead( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> VeilidAPIResult>; + fn encrypt_in_place_aead( + &self, + body: &mut Vec, + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> VeilidAPIResult<()>; + fn encrypt_aead( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> VeilidAPIResult>; + + // NoAuth Encrypt/Decrypt + fn crypt_in_place_no_auth( + &self, + body: &mut [u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> VeilidAPIResult<()>; + fn crypt_b2b_no_auth( + &self, + in_buf: &[u8], + out_buf: &mut [u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> VeilidAPIResult<()>; + fn crypt_no_auth_aligned_8( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> VeilidAPIResult>; + fn crypt_no_auth_unaligned( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> VeilidAPIResult>; +} diff --git a/veilid-core/src/crypto/none/mod.rs b/veilid-core/src/crypto/crypto_system/none/mod.rs similarity index 63% rename from veilid-core/src/crypto/none/mod.rs rename to veilid-core/src/crypto/crypto_system/none/mod.rs index d8b70465..e6a18c82 100644 --- a/veilid-core/src/crypto/none/mod.rs +++ b/veilid-core/src/crypto/crypto_system/none/mod.rs @@ -6,11 +6,11 @@ use data_encoding::BASE64URL_NOPAD; use digest::rand_core::RngCore; use digest::Digest; const NONE_AEAD_OVERHEAD: usize = NONE_PUBLIC_KEY_LENGTH; -pub const CRYPTO_KIND_NONE: CryptoKind = CryptoKind(*b"NONE"); +pub const CRYPTO_KIND_NONE: CryptoKind = CryptoKind::new(*b"NONE"); pub const CRYPTO_KIND_NONE_FOURCC: u32 = u32::from_be_bytes(*b"NONE"); pub use sizes::*; -pub fn none_generate_keypair() -> BareKeyPair { +pub fn none_generate_keypair() -> KeyPair { let mut csprng = VeilidRng {}; let mut pub_bytes = [0u8; NONE_PUBLIC_KEY_LENGTH]; let mut sec_bytes = [0u8; NONE_SECRET_KEY_LENGTH]; @@ -20,7 +20,7 @@ pub fn none_generate_keypair() -> BareKeyPair { } let dht_key = BarePublicKey::new(&pub_bytes); let dht_key_secret = BareSecretKey::new(&sec_bytes); - BareKeyPair::new(dht_key, dht_key_secret) + KeyPair::new(CRYPTO_KIND_NONE, BareKeyPair::new(dht_key, dht_key_secret)) } fn do_xor_32(a: &[u8], b: &[u8]) -> VeilidAPIResult<[u8; 32]> { @@ -93,11 +93,7 @@ impl CryptoSystem for CryptoSystemNONE { } // Cached Operations - fn cached_dh( - &self, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult { + fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult { self.crypto() .cached_dh_internal::(self, key, secret) } @@ -128,50 +124,51 @@ impl CryptoSystem for CryptoSystemNONE { Ok(self.hash_password(password, &salt)? == password_hash) } - fn derive_shared_secret( - &self, - password: &[u8], - salt: &[u8], - ) -> VeilidAPIResult { + fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult { if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH { apibail_generic!("invalid salt length"); } - Ok(BareSharedSecret::new( - blake3::hash(self.hash_password(password, salt)?.as_bytes()).as_bytes(), + Ok(SharedSecret::new( + CRYPTO_KIND_NONE, + BareSharedSecret::new( + blake3::hash(self.hash_password(password, salt)?.as_bytes()).as_bytes(), + ), )) } - fn random_nonce(&self) -> BareNonce { + fn random_nonce(&self) -> Nonce { let mut nonce = [0u8; NONE_NONCE_LENGTH]; random_bytes(&mut nonce); - BareNonce::new(&nonce) + Nonce::new(&nonce) } - fn random_shared_secret(&self) -> BareSharedSecret { + fn random_shared_secret(&self) -> SharedSecret { let mut s = [0u8; NONE_SHARED_SECRET_LENGTH]; random_bytes(&mut s); - BareSharedSecret::new(&s) + SharedSecret::new(CRYPTO_KIND_NONE, BareSharedSecret::new(&s)) } - fn compute_dh( - &self, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult { - let s = do_xor_32(key, secret)?; - Ok(BareSharedSecret::new(&s)) + fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult { + let s = do_xor_32(key.ref_value(), secret.ref_value())?; + Ok(SharedSecret::new( + CRYPTO_KIND_NONE, + BareSharedSecret::new(&s), + )) } - fn generate_keypair(&self) -> BareKeyPair { + fn generate_keypair(&self) -> KeyPair { none_generate_keypair() } - fn generate_hash(&self, data: &[u8]) -> BareHashDigest { - BareHashDigest::new(blake3::hash(data).as_bytes()) + fn generate_hash(&self, data: &[u8]) -> HashDigest { + HashDigest::new( + CRYPTO_KIND_NONE, + BareHashDigest::new(blake3::hash(data).as_bytes()), + ) } - fn generate_hash_reader( - &self, - reader: &mut dyn std::io::Read, - ) -> VeilidAPIResult { + fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult { let mut hasher = blake3::Hasher::new(); std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?; - Ok(BarePublicKey::new(hasher.finalize().as_bytes())) + Ok(PublicKey::new( + CRYPTO_KIND_NONE, + BarePublicKey::new(hasher.finalize().as_bytes()), + )) } // Validation @@ -200,49 +197,56 @@ impl CryptoSystem for CryptoSystemNONE { NONE_SIGNATURE_LENGTH } - fn validate_keypair(&self, dht_key: &BarePublicKey, dht_key_secret: &BareSecretKey) -> bool { + fn validate_keypair( + &self, + public_key: &PublicKey, + secret_key: &SecretKey, + ) -> VeilidAPIResult { + self.check_public_key(public_key)?; + self.check_secret_key(secret_key)?; + let data = vec![0u8; 512]; - let Ok(sig) = self.sign(dht_key, dht_key_secret, &data) else { - return false; + let Ok(sig) = self.sign(public_key, secret_key, &data) else { + return Ok(false); }; - let Ok(v) = self.verify(dht_key, &data, &sig) else { - return false; + let Ok(v) = self.verify(public_key, &data, &sig) else { + return Ok(false); }; - v + Ok(v) } - fn validate_hash(&self, data: &[u8], dht_key: &BareHashDigest) -> bool { - let bytes = *blake3::hash(data).as_bytes(); - bytes == dht_key.bytes() + fn validate_hash(&self, data: &[u8], hash_digest: &HashDigest) -> VeilidAPIResult { + self.check_hash_digest(hash_digest)?; + let out_hash = blake3::hash(data); + let bytes = out_hash.as_bytes(); + Ok(*bytes == **hash_digest.ref_value()) } fn validate_hash_reader( &self, reader: &mut dyn std::io::Read, - dht_key: &BareHashDigest, + hash_digest: &HashDigest, ) -> VeilidAPIResult { + self.check_hash_digest(hash_digest)?; let mut hasher = blake3::Hasher::new(); std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?; - let bytes = *hasher.finalize().as_bytes(); - Ok(bytes == dht_key.bytes()) - } - // Distance Metric - fn distance(&self, key1: &BareHashDigest, key2: &BareHashDigest) -> BareHashDistance { - let mut bytes = [0u8; NONE_HASH_DIGEST_LENGTH]; - - for (n, byte) in bytes.iter_mut().enumerate() { - *byte = key1[n] ^ key2[n]; - } - - BareHashDistance::new(&bytes) + let out_hash = hasher.finalize(); + let bytes = out_hash.as_bytes(); + Ok(*bytes == **hash_digest.ref_value()) } // Authentication fn sign( &self, - dht_key: &BarePublicKey, - dht_key_secret: &BareSecretKey, + public_key: &PublicKey, + secret_key: &SecretKey, data: &[u8], - ) -> VeilidAPIResult { - if !is_bytes_eq_32(&do_xor_32(dht_key, dht_key_secret)?, 0xFFu8)? { + ) -> VeilidAPIResult { + self.check_public_key(public_key)?; + self.check_secret_key(secret_key)?; + + if !is_bytes_eq_32( + &do_xor_32(public_key.ref_value(), secret_key.ref_value())?, + 0xFFu8, + )? { return Err(VeilidAPIError::parse_error( "Keypair is invalid", "invalid keys", @@ -255,31 +259,43 @@ impl CryptoSystem for CryptoSystemNONE { let in_sig_bytes: [u8; NONE_SIGNATURE_LENGTH] = sig.into(); let mut sig_bytes = [0u8; NONE_SIGNATURE_LENGTH]; sig_bytes[0..32].copy_from_slice(&in_sig_bytes[0..32]); - sig_bytes[32..64].copy_from_slice(&do_xor_32(&in_sig_bytes[32..64], dht_key_secret)?); - let dht_sig = BareSignature::new(&sig_bytes); + sig_bytes[32..64] + .copy_from_slice(&do_xor_32(&in_sig_bytes[32..64], secret_key.ref_value())?); + let dht_sig = Signature::new(CRYPTO_KIND_NONE, BareSignature::new(&sig_bytes)); println!("DEBUG dht_sig: {:?}", dht_sig); Ok(dht_sig) } fn verify( &self, - dht_key: &BarePublicKey, + public_key: &PublicKey, data: &[u8], - signature: &BareSignature, + signature: &Signature, ) -> VeilidAPIResult { + self.check_public_key(public_key)?; + self.check_signature(signature)?; + let mut dig = Blake3Digest512::new(); dig.update(data); let sig = dig.finalize(); let in_sig_bytes: [u8; NONE_SIGNATURE_LENGTH] = sig.into(); let mut verify_bytes = [0u8; NONE_SIGNATURE_LENGTH]; - verify_bytes[0..32].copy_from_slice(&do_xor_32(&in_sig_bytes[0..32], &signature[0..32])?); - verify_bytes[32..64] - .copy_from_slice(&do_xor_32(&in_sig_bytes[32..64], &signature[32..64])?); + verify_bytes[0..32].copy_from_slice(&do_xor_32( + &in_sig_bytes[0..32], + &signature.ref_value()[0..32], + )?); + verify_bytes[32..64].copy_from_slice(&do_xor_32( + &in_sig_bytes[32..64], + &signature.ref_value()[32..64], + )?); if !is_bytes_eq_32(&verify_bytes[0..32], 0u8)? { return Ok(false); } - if !is_bytes_eq_32(&do_xor_32(&verify_bytes[32..64], dht_key)?, 0xFFu8)? { + if !is_bytes_eq_32( + &do_xor_32(&verify_bytes[32..64], public_key.ref_value())?, + 0xFFu8, + )? { return Ok(false); } @@ -290,13 +306,16 @@ impl CryptoSystem for CryptoSystemNONE { fn decrypt_in_place_aead( &self, body: &mut Vec, - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, _associated_data: Option<&[u8]>, ) -> VeilidAPIResult<()> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut blob = nonce.to_vec(); blob.extend_from_slice(&[0u8; 8]); - let blob = do_xor_32(&blob, shared_secret)?; + let blob = do_xor_32(&blob, shared_secret.ref_value())?; if body.len() < NONE_AEAD_OVERHEAD { return Err(VeilidAPIError::generic("invalid length")); @@ -311,10 +330,13 @@ impl CryptoSystem for CryptoSystemNONE { fn decrypt_aead( &self, body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut out = body.to_vec(); self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data) .map_err(map_to_string) @@ -325,13 +347,16 @@ impl CryptoSystem for CryptoSystemNONE { fn encrypt_in_place_aead( &self, body: &mut Vec, - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, _associated_data: Option<&[u8]>, ) -> VeilidAPIResult<()> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut blob = nonce.to_vec(); blob.extend_from_slice(&[0u8; 8]); - let blob = do_xor_32(&blob, shared_secret)?; + let blob = do_xor_32(&blob, shared_secret.ref_value())?; do_xor_inplace(body, &blob)?; body.append(&mut blob.to_vec()); Ok(()) @@ -340,10 +365,13 @@ impl CryptoSystem for CryptoSystemNONE { fn encrypt_aead( &self, body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut out = body.to_vec(); self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data) .map_err(map_to_string) @@ -355,12 +383,15 @@ impl CryptoSystem for CryptoSystemNONE { fn crypt_in_place_no_auth( &self, body: &mut [u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult<()> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut blob = nonce.to_vec(); blob.extend_from_slice(&[0u8; 8]); - let blob = do_xor_32(&blob, shared_secret)?; + let blob = do_xor_32(&blob, shared_secret.ref_value())?; do_xor_inplace(body, &blob) } @@ -368,21 +399,27 @@ impl CryptoSystem for CryptoSystemNONE { &self, in_buf: &[u8], out_buf: &mut [u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult<()> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut blob = nonce.to_vec(); blob.extend_from_slice(&[0u8; 8]); - let blob = do_xor_32(&blob, shared_secret)?; + let blob = do_xor_32(&blob, shared_secret.ref_value())?; do_xor_b2b(in_buf, out_buf, &blob) } fn crypt_no_auth_aligned_8( &self, in_buf: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) }; self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?; Ok(out_buf) @@ -391,9 +428,12 @@ impl CryptoSystem for CryptoSystemNONE { fn crypt_no_auth_unaligned( &self, in_buf: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) }; self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?; Ok(out_buf) diff --git a/veilid-core/src/crypto/none/sizes.rs b/veilid-core/src/crypto/crypto_system/none/sizes.rs similarity index 100% rename from veilid-core/src/crypto/none/sizes.rs rename to veilid-core/src/crypto/crypto_system/none/sizes.rs diff --git a/veilid-core/src/crypto/vld0/mod.rs b/veilid-core/src/crypto/crypto_system/vld0/mod.rs similarity index 76% rename from veilid-core/src/crypto/vld0/mod.rs rename to veilid-core/src/crypto/crypto_system/vld0/mod.rs index cc5159e3..50dcada8 100644 --- a/veilid-core/src/crypto/vld0/mod.rs +++ b/veilid-core/src/crypto/crypto_system/vld0/mod.rs @@ -19,13 +19,14 @@ const VLD0_DOMAIN_SIGN: &[u8] = b"VLD0_SIGN"; const VLD0_DOMAIN_CRYPT: &[u8] = b"VLD0_CRYPT"; const VLD0_AEAD_OVERHEAD: usize = 16; -pub const CRYPTO_KIND_VLD0: CryptoKind = CryptoKind(*b"VLD0"); +pub const CRYPTO_KIND_VLD0: CryptoKind = CryptoKind::new(*b"VLD0"); pub const CRYPTO_KIND_VLD0_FOURCC: u32 = u32::from_be_bytes(*b"VLD0"); pub use sizes::*; -fn public_to_x25519_pk(public: &BarePublicKey) -> VeilidAPIResult { +fn public_to_x25519_pk(public: &PublicKey) -> VeilidAPIResult { let pk_ed = ed::VerifyingKey::from_bytes( public + .ref_value() .bytes() .try_into() .map_err(VeilidAPIError::internal)?, @@ -33,11 +34,11 @@ fn public_to_x25519_pk(public: &BarePublicKey) -> VeilidAPIResult .map_err(VeilidAPIError::internal)?; Ok(xd::PublicKey::from(*pk_ed.to_montgomery().as_bytes())) } -fn secret_to_x25519_sk(secret: &BareSecretKey) -> VeilidAPIResult { +fn secret_to_x25519_sk(secret: &SecretKey) -> VeilidAPIResult { // NOTE: ed::SigningKey.to_scalar() does not produce an unreduced scalar, we want the raw bytes here // See https://github.com/dalek-cryptography/curve25519-dalek/issues/565 let hash: [u8; VLD0_SIGNATURE_LENGTH] = ed::Sha512::default() - .chain_update(secret.bytes()) + .chain_update(secret.ref_value().bytes()) .finalize() .into(); let mut output = [0u8; VLD0_SECRET_KEY_LENGTH]; @@ -46,14 +47,14 @@ fn secret_to_x25519_sk(secret: &BareSecretKey) -> VeilidAPIResult BareKeyPair { +pub(crate) fn vld0_generate_keypair() -> KeyPair { let mut csprng = VeilidRng {}; let signing_key = ed::SigningKey::generate(&mut csprng); let verifying_key = signing_key.verifying_key(); let public_key = BarePublicKey::new(&verifying_key.to_bytes()); let secret_key = BareSecretKey::new(&signing_key.to_bytes()); - BareKeyPair::new(public_key, secret_key) + KeyPair::new(CRYPTO_KIND_VLD0, BareKeyPair::new(public_key, secret_key)) } /// V0 CryptoSystem @@ -80,11 +81,7 @@ impl CryptoSystem for CryptoSystemVLD0 { // Cached Operations #[instrument(level = "trace", skip_all)] - fn cached_dh( - &self, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult { + fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult { self.crypto() .cached_dh_internal::(self, key, secret) } @@ -125,11 +122,7 @@ impl CryptoSystem for CryptoSystemVLD0 { } #[instrument(level = "trace", target = "crypto", skip_all)] - fn derive_shared_secret( - &self, - password: &[u8], - salt: &[u8], - ) -> VeilidAPIResult { + fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult { if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH { apibail_generic!("invalid salt length"); } @@ -141,29 +134,28 @@ impl CryptoSystem for CryptoSystemVLD0 { argon2 .hash_password_into(password, salt, &mut output_key_material) .map_err(VeilidAPIError::generic)?; - Ok(BareSharedSecret::new(&output_key_material)) + Ok(SharedSecret::new( + CRYPTO_KIND_VLD0, + BareSharedSecret::new(&output_key_material), + )) } #[instrument(level = "trace", target = "crypto", skip_all)] - fn random_nonce(&self) -> BareNonce { + fn random_nonce(&self) -> Nonce { let mut nonce = [0u8; VLD0_NONCE_LENGTH]; random_bytes(&mut nonce); - BareNonce::new(&nonce) + Nonce::new(&nonce) } #[instrument(level = "trace", target = "crypto", skip_all)] - fn random_shared_secret(&self) -> BareSharedSecret { + fn random_shared_secret(&self) -> SharedSecret { let mut s = [0u8; VLD0_SHARED_SECRET_LENGTH]; random_bytes(&mut s); - BareSharedSecret::new(&s) + SharedSecret::new(CRYPTO_KIND_VLD0, BareSharedSecret::new(&s)) } #[instrument(level = "trace", target = "crypto", skip_all)] - fn compute_dh( - &self, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult { + fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult { let pk_xd = public_to_x25519_pk(key)?; let sk_xd = secret_to_x25519_sk(secret)?; @@ -174,27 +166,33 @@ impl CryptoSystem for CryptoSystemVLD0 { hasher.update(&dh_bytes); let output = hasher.finalize(); - Ok(BareSharedSecret::new(output.as_bytes())) + Ok(SharedSecret::new( + CRYPTO_KIND_VLD0, + BareSharedSecret::new(output.as_bytes()), + )) } #[instrument(level = "trace", target = "crypto", skip_all)] - fn generate_keypair(&self) -> BareKeyPair { + fn generate_keypair(&self) -> KeyPair { vld0_generate_keypair() } #[instrument(level = "trace", target = "crypto", skip_all)] - fn generate_hash(&self, data: &[u8]) -> BareHashDigest { - BareHashDigest::new(blake3::hash(data).as_bytes()) + fn generate_hash(&self, data: &[u8]) -> HashDigest { + HashDigest::new( + CRYPTO_KIND_VLD0, + BareHashDigest::new(blake3::hash(data).as_bytes()), + ) } #[instrument(level = "trace", target = "crypto", skip_all)] - fn generate_hash_reader( - &self, - reader: &mut dyn std::io::Read, - ) -> VeilidAPIResult { + fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult { let mut hasher = blake3::Hasher::new(); std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?; - Ok(BarePublicKey::new(hasher.finalize().as_bytes())) + Ok(PublicKey::new( + CRYPTO_KIND_VLD0, + BarePublicKey::new(hasher.finalize().as_bytes()), + )) } // Validation @@ -224,61 +222,63 @@ impl CryptoSystem for CryptoSystemVLD0 { } #[instrument(level = "trace", target = "crypto", skip_all)] - fn validate_keypair(&self, public_key: &BarePublicKey, secret_key: &BareSecretKey) -> bool { + fn validate_keypair( + &self, + public_key: &PublicKey, + secret_key: &SecretKey, + ) -> VeilidAPIResult { + self.check_public_key(public_key)?; + self.check_secret_key(secret_key)?; + let data = vec![0u8; 512]; let Ok(sig) = self.sign(public_key, secret_key, &data) else { - return false; + return Ok(false); }; let Ok(v) = self.verify(public_key, &data, &sig) else { - return false; + return Ok(false); }; - v + Ok(v) } #[instrument(level = "trace", target = "crypto", skip_all)] - fn validate_hash(&self, data: &[u8], hash_digest: &BareHashDigest) -> bool { + fn validate_hash(&self, data: &[u8], hash_digest: &HashDigest) -> VeilidAPIResult { + self.check_hash_digest(hash_digest)?; + let bytes = *blake3::hash(data).as_bytes(); - bytes == hash_digest.bytes() + Ok(bytes == hash_digest.ref_value().bytes()) } #[instrument(level = "trace", target = "crypto", skip_all)] fn validate_hash_reader( &self, reader: &mut dyn std::io::Read, - hash_digest: &BareHashDigest, + hash_digest: &HashDigest, ) -> VeilidAPIResult { + self.check_hash_digest(hash_digest)?; + let mut hasher = blake3::Hasher::new(); std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?; let bytes = *hasher.finalize().as_bytes(); - Ok(bytes == hash_digest.bytes()) - } - - // Distance Metric - #[instrument(level = "trace", target = "crypto", skip_all)] - fn distance(&self, hash1: &BareHashDigest, hash2: &BareHashDigest) -> BareHashDistance { - let mut bytes = [0u8; VLD0_HASH_DIGEST_LENGTH]; - - (0..VLD0_HASH_DIGEST_LENGTH).for_each(|n| { - bytes[n] = hash1[n] ^ hash2[n]; - }); - - BareHashDistance::new(&bytes) + Ok(bytes == hash_digest.ref_value().bytes()) } // Authentication #[instrument(level = "trace", target = "crypto", skip_all)] fn sign( &self, - public_key: &BarePublicKey, - secret_key: &BareSecretKey, + public_key: &PublicKey, + secret_key: &SecretKey, data: &[u8], - ) -> VeilidAPIResult { + ) -> VeilidAPIResult { + self.check_public_key(public_key)?; + self.check_secret_key(secret_key)?; + let mut kpb: [u8; VLD0_SECRET_KEY_LENGTH + VLD0_PUBLIC_KEY_LENGTH] = [0u8; VLD0_SECRET_KEY_LENGTH + VLD0_PUBLIC_KEY_LENGTH]; - kpb[..VLD0_SECRET_KEY_LENGTH].copy_from_slice(secret_key); - kpb[VLD0_SECRET_KEY_LENGTH..].copy_from_slice(public_key); + kpb[..VLD0_SECRET_KEY_LENGTH].copy_from_slice(secret_key.ref_value().bytes()); + kpb[VLD0_SECRET_KEY_LENGTH..].copy_from_slice(public_key.ref_value().bytes()); let keypair = ed::SigningKey::from_keypair_bytes(&kpb) .map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?; @@ -289,7 +289,7 @@ impl CryptoSystem for CryptoSystemVLD0 { .sign_prehashed(dig, Some(VLD0_DOMAIN_SIGN)) .map_err(VeilidAPIError::internal)?; - let sig = BareSignature::new(&sig_bytes.to_bytes()); + let sig = Signature::new(CRYPTO_KIND_VLD0, BareSignature::new(&sig_bytes.to_bytes())); if !self.verify(public_key, data, &sig)? { apibail_internal!("newly created signature does not verify"); @@ -300,12 +300,16 @@ impl CryptoSystem for CryptoSystemVLD0 { #[instrument(level = "trace", target = "crypto", skip_all)] fn verify( &self, - public_key: &BarePublicKey, + public_key: &PublicKey, data: &[u8], - signature: &BareSignature, + signature: &Signature, ) -> VeilidAPIResult { + self.check_public_key(public_key)?; + self.check_signature(signature)?; + let pk = ed::VerifyingKey::from_bytes( public_key + .ref_value() .bytes() .try_into() .map_err(VeilidAPIError::internal)?, @@ -313,6 +317,7 @@ impl CryptoSystem for CryptoSystemVLD0 { .map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?; let sig = ed::Signature::from_bytes( signature + .ref_value() .bytes() .try_into() .map_err(VeilidAPIError::internal)?, @@ -335,11 +340,14 @@ impl CryptoSystem for CryptoSystemVLD0 { fn decrypt_in_place_aead( &self, body: &mut Vec, - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult<()> { + self.check_shared_secret(shared_secret)?; + let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret + .ref_value() .bytes() .try_into() .map_err(VeilidAPIError::internal)?; @@ -358,10 +366,13 @@ impl CryptoSystem for CryptoSystemVLD0 { fn decrypt_aead( &self, body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut out = body.to_vec(); self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data) .map_err(map_to_string) @@ -373,11 +384,15 @@ impl CryptoSystem for CryptoSystemVLD0 { fn encrypt_in_place_aead( &self, body: &mut Vec, - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult<()> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret + .ref_value() .bytes() .try_into() .map_err(VeilidAPIError::internal)?; @@ -397,10 +412,13 @@ impl CryptoSystem for CryptoSystemVLD0 { fn encrypt_aead( &self, body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut out = body.to_vec(); self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data) .map_err(map_to_string) @@ -413,10 +431,14 @@ impl CryptoSystem for CryptoSystemVLD0 { fn crypt_in_place_no_auth( &self, body: &mut [u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult<()> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret + .ref_value() .bytes() .try_into() .map_err(VeilidAPIError::internal)?; @@ -435,10 +457,14 @@ impl CryptoSystem for CryptoSystemVLD0 { &self, in_buf: &[u8], out_buf: &mut [u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult<()> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret + .ref_value() .bytes() .try_into() .map_err(VeilidAPIError::internal)?; @@ -458,9 +484,12 @@ impl CryptoSystem for CryptoSystemVLD0 { fn crypt_no_auth_aligned_8( &self, in_buf: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) }; self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?; Ok(out_buf) @@ -470,9 +499,12 @@ impl CryptoSystem for CryptoSystemVLD0 { fn crypt_no_auth_unaligned( &self, in_buf: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult> { + self.check_nonce(nonce)?; + self.check_shared_secret(shared_secret)?; + let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) }; self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?; Ok(out_buf) diff --git a/veilid-core/src/crypto/vld0/sizes.rs b/veilid-core/src/crypto/crypto_system/vld0/sizes.rs similarity index 100% rename from veilid-core/src/crypto/vld0/sizes.rs rename to veilid-core/src/crypto/crypto_system/vld0/sizes.rs diff --git a/veilid-core/src/crypto/vld1/mod.rs b/veilid-core/src/crypto/crypto_system/vld1/mod.rs similarity index 55% rename from veilid-core/src/crypto/vld1/mod.rs rename to veilid-core/src/crypto/crypto_system/vld1/mod.rs index e1ae99d9..e0ee03ff 100644 --- a/veilid-core/src/crypto/vld1/mod.rs +++ b/veilid-core/src/crypto/crypto_system/vld1/mod.rs @@ -1,4 +1,4 @@ use super::*; -pub const CRYPTO_KIND_VLD1: CryptoKind = CryptoKind(*b"VLD1"); +pub const CRYPTO_KIND_VLD1: CryptoKind = CryptoKind::new(*b"VLD1"); pub const CRYPTO_KIND_VLD1_FOURCC: u32 = u32::from_be_bytes(*b"VLD1"); diff --git a/veilid-core/src/crypto/dh_cache.rs b/veilid-core/src/crypto/dh_cache.rs index 86b64c08..d959a115 100644 --- a/veilid-core/src/crypto/dh_cache.rs +++ b/veilid-core/src/crypto/dh_cache.rs @@ -1,45 +1,62 @@ use super::*; use crate::*; -// Diffie-Hellman key exchange cache -#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)] +// Diffie-Hellman key agreement cache +#[derive(Debug, PartialEq, Eq, Hash)] pub struct DHCacheKey { - pub key: BarePublicKey, - pub secret: BareSecretKey, + pub key: PublicKey, + pub secret: SecretKey, } -#[derive(Serialize, Deserialize)] +impl fmt::Display for DHCacheKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}", self.key, self.secret) + } +} + +impl FromStr for DHCacheKey { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let Some((pks, sks)) = s.split_once('.') else { + apibail_parse_error!("s", s); + }; + let key = PublicKey::from_str(pks)?; + let secret = SecretKey::from_str(sks)?; + Ok(DHCacheKey { key, secret }) + } +} + +impl<'de> Deserialize<'de> for DHCacheKey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(de::Error::custom) + } +} + +impl Serialize for DHCacheKey { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +#[derive(Debug, Serialize, Deserialize)] pub struct DHCacheValue { - pub shared_secret: BareSharedSecret, + pub shared_secret: SharedSecret, } pub type DHCache = LruCache; pub const DH_CACHE_SIZE: usize = 4096; pub fn cache_to_bytes(cache: &DHCache) -> Vec { - let cnt: usize = cache.len(); - let mut out: Vec = Vec::with_capacity(cnt * (32 + 32 + 32)); - for e in cache.iter() { - out.extend_from_slice(&e.0.key); - out.extend_from_slice(&e.0.secret); - out.extend_from_slice(&e.1.shared_secret); - } - let mut rev: Vec = Vec::with_capacity(out.len()); - for d in out.chunks(32 + 32 + 32).rev() { - rev.extend(d); - } - rev + serialize_json_bytes(cache) } -pub fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) { - for d in bytes.chunks(32 + 32 + 32) { - let k = DHCacheKey { - key: BarePublicKey::new(d[0..32].try_into().expect("asdf")), - secret: BareSecretKey::new(d[32..64].try_into().expect("asdf")), - }; - let v = DHCacheValue { - shared_secret: BareSharedSecret::new(d[64..96].try_into().expect("asdf")), - }; - cache.insert(k, v); - } +pub fn bytes_to_cache(bytes: &[u8]) -> EyreResult { + deserialize_json_bytes(bytes).wrap_err("cache format invalid") } diff --git a/veilid-core/src/crypto/envelope.rs b/veilid-core/src/crypto/envelope.rs deleted file mode 100644 index 3fa3ef31..00000000 --- a/veilid-core/src/crypto/envelope.rs +++ /dev/null @@ -1,364 +0,0 @@ -#![allow(clippy::absurd_extreme_comparisons)] -use super::*; -use crate::*; -use core::convert::TryInto; - -pub const MAX_ENVELOPE_SIZE: usize = 65507; -pub const MIN_ENVELOPE_SIZE: usize = 0x6A + 0x40; // Header + BareSignature - -// Version number of envelope format -fourcc_type!(EnvelopeVersion); - -pub const ENVELOPE_VERSION_VLD0: EnvelopeVersion = EnvelopeVersion(*b"ENV0"); -// pub const ENVELOPE_VERSION_VLD0_FOURCC: u32 = u32::from_be_bytes(*b"ENV0"); - -/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one -pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [ENVELOPE_VERSION_VLD0]; -/// Number of envelope versions to keep on structures if many are present beyond the ones we consider valid -pub const MAX_ENVELOPE_VERSIONS: usize = 16; - -/// Envelopes are versioned -/// -/// These are the formats for the on-the-wire serialization performed by this module -/// -/// #[repr(C, packed)] -/// struct EnvelopeHeader { -/// // Size is at least 4 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct -/// version: [u8; 4], // 0x00: 0x45 0x4E 0x56 0x30 ("ENV0") -/// } -/// -/// #[repr(C, packed)] -/// struct EnvelopeV0 { -/// // Size is 106 bytes without signature and 170 with signature -/// version: [u8; 4], // 0x00: 0x45 0x4E 0x56 0x30 ("ENV0") -/// crypto_kind: [u8; 4], // 0x04: CryptoSystemVersion FOURCC code (CryptoKind) -/// size: u16, // 0x08: Total size of the envelope including the encrypted operations message. Maximum size is 65,507 bytes, which is the data size limit for a single UDP message on IPv4. -/// timestamp: u64, // 0x0A: Duration since UNIX_EPOCH in microseconds when this message is sent. Messages older than 10 seconds are dropped. -/// nonce: [u8; 24], // 0x12: Random nonce for replay protection and for dh -/// sender_id: [u8; 32], // 0x2A: Node ID of the message source, which is the public key of the sender (must be verified with find_node if this is a new node_id/address combination) -/// recipient_id: [u8; 32], // 0x4A: Node ID of the intended recipient, which is the public key of the recipient (must be the receiving node, or a relay lease holder) -/// // 0x6A: message is appended (operations) -/// signature: [u8; 64], // 0x?? (end-0x40): BareSignature of the entire envelope including header is appended to the packet -/// // entire header needs to be included in message digest, relays are not allowed to modify the envelope without invalidating the signature. -/// } -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct Envelope { - version: EnvelopeVersion, - crypto_kind: CryptoKind, - timestamp: Timestamp, - nonce: BareNonce, - sender_id: BareNodeId, - recipient_id: BareNodeId, -} - -impl Envelope { - #[must_use] - pub fn new( - version: EnvelopeVersion, - crypto_kind: CryptoKind, - timestamp: Timestamp, - nonce: BareNonce, - sender_id: BareNodeId, - recipient_id: BareNodeId, - ) -> Self { - assert!(VALID_ENVELOPE_VERSIONS.contains(&version)); - assert!(VALID_CRYPTO_KINDS.contains(&crypto_kind)); - Self { - version, - crypto_kind, - timestamp, - nonce, - sender_id, - recipient_id, - } - } - - #[instrument(level = "trace", target = "envelope", skip_all)] - pub fn from_signed_data( - crypto: &Crypto, - data: &[u8], - network_key: &Option, - ) -> VeilidAPIResult { - // Ensure we are at least the length of the envelope - // Silent drop here, as we use zero length packets as part of the protocol for hole punching - if data.len() < MIN_ENVELOPE_SIZE { - apibail_generic!("envelope data too small"); - } - - // Check envelope version - let version: EnvelopeVersion = data[0x00..0x04] - .try_into() - .map_err(VeilidAPIError::internal)?; - if !VALID_ENVELOPE_VERSIONS.contains(&version) { - apibail_parse_error!("unsupported envelope version", version); - } - - // Check crypto kind - let crypto_kind = CryptoKind( - data[0x04..0x08] - .try_into() - .map_err(VeilidAPIError::internal)?, - ); - let Some(vcrypto) = crypto.get(crypto_kind) else { - apibail_parse_error!("unsupported crypto kind", crypto_kind); - }; - - // Get size and ensure it matches the size of the envelope and is less than the maximum message size - let size: u16 = u16::from_le_bytes( - data[0x08..0x0A] - .try_into() - .map_err(VeilidAPIError::internal)?, - ); - if (size as usize) > MAX_ENVELOPE_SIZE { - apibail_parse_error!("envelope too large", size); - } - if (size as usize) != data.len() { - apibail_parse_error!( - "size doesn't match envelope size", - format!( - "size doesn't match envelope size: size={} data.len()={}", - size, - data.len() - ) - ); - } - - // Get the timestamp - let timestamp: Timestamp = u64::from_le_bytes( - data[0x0A..0x12] - .try_into() - .map_err(VeilidAPIError::internal)?, - ) - .into(); - - // Get nonce and sender node id - let mut nonce_slice: [u8; VLD0_NONCE_LENGTH] = data[0x12..0x2A] - .try_into() - .map_err(VeilidAPIError::internal)?; - let mut sender_id_slice: [u8; VLD0_HASH_DIGEST_LENGTH] = data[0x2A..0x4A] - .try_into() - .map_err(VeilidAPIError::internal)?; - let mut recipient_id_slice: [u8; VLD0_HASH_DIGEST_LENGTH] = data[0x4A..0x6A] - .try_into() - .map_err(VeilidAPIError::internal)?; - - // Apply network key (not the best, but it will keep networks from colliding without much overhead) - if let Some(nk) = network_key.as_ref() { - for n in 0..VLD0_NONCE_LENGTH { - nonce_slice[n] ^= nk[n]; - } - for n in 0..VLD0_HASH_DIGEST_LENGTH { - sender_id_slice[n] ^= nk[n]; - } - for n in 0..VLD0_HASH_DIGEST_LENGTH { - recipient_id_slice[n] ^= nk[n]; - } - } - - let nonce: BareNonce = BareNonce::new(&nonce_slice); - let sender_id = BareNodeId::new(&sender_id_slice); - let recipient_id = BareNodeId::new(&recipient_id_slice); - - // Ensure sender_id and recipient_id are not the same - if sender_id == recipient_id { - apibail_parse_error!( - "sender_id should not be same as recipient_id", - recipient_id.encode() - ); - } - - // Get signature - let signature = BareSignature::new( - data[(data.len() - 64)..] - .try_into() - .map_err(VeilidAPIError::internal)?, - ); - - // Validate signature - if !vcrypto - .verify( - &sender_id.clone().into(), - &data[0..(data.len() - 64)], - &signature, - ) - .map_err(VeilidAPIError::internal)? - { - apibail_parse_error!("signature verification of envelope failed", signature); - } - - // Return envelope - Ok(Self { - version, - crypto_kind, - timestamp, - nonce, - sender_id, - recipient_id, - }) - } - - #[instrument(level = "trace", target = "envelope", skip_all)] - pub fn decrypt_body( - &self, - crypto: &Crypto, - data: &[u8], - secret_key: &BareSecretKey, - network_key: &Option, - ) -> VeilidAPIResult> { - // Get DH secret - let vcrypto = crypto - .get(self.crypto_kind) - .expect("need to ensure only valid crypto kinds here"); - let mut dh_secret = vcrypto.cached_dh(&self.sender_id.clone().into(), secret_key)?; - - // Apply network key - if let Some(nk) = network_key.as_ref() { - let mut dh_secret_bytes = dh_secret.to_vec(); - - for n in 0..VLD0_SHARED_SECRET_LENGTH { - dh_secret_bytes[n] ^= nk[n]; - } - - dh_secret = BareSharedSecret::new(&dh_secret_bytes); - } - // Decrypt message without authentication - let body = vcrypto.crypt_no_auth_aligned_8( - &data[0x6A..data.len() - 64], - &self.nonce, - &dh_secret, - )?; - - // Decompress body - let body = decompress_size_prepended(&body, Some(MAX_ENVELOPE_SIZE))?; - - Ok(body) - } - - #[instrument(level = "trace", target = "envelope", skip_all, err)] - pub fn to_encrypted_data( - &self, - crypto: &Crypto, - body: &[u8], - secret_key: &BareSecretKey, - network_key: &Option, - ) -> VeilidAPIResult> { - // Ensure body isn't too long - let uncompressed_body_size: usize = body.len() + MIN_ENVELOPE_SIZE; - if uncompressed_body_size > MAX_ENVELOPE_SIZE { - apibail_parse_error!( - "envelope size before compression is too large", - uncompressed_body_size - ); - } - - // Compress body - let body = compress_prepend_size(body); - - // Ensure body isn't too long - let envelope_size: usize = body.len() + MIN_ENVELOPE_SIZE; - if envelope_size > MAX_ENVELOPE_SIZE { - apibail_parse_error!( - "envelope size after compression is too large", - envelope_size - ); - } - // Generate dh secret - let vcrypto = crypto - .get(self.crypto_kind) - .expect("need to ensure only valid crypto kinds here"); - let mut dh_secret = vcrypto.cached_dh(&self.recipient_id.clone().into(), secret_key)?; - - // Write envelope body - let mut data = vec![0u8; envelope_size]; - - // Write version - data[0x00..0x04].copy_from_slice(&self.version.0); - // Write crypto kind - data[0x04..0x08].copy_from_slice(&self.crypto_kind.0); - // Write size - data[0x08..0x0A].copy_from_slice(&(envelope_size as u16).to_le_bytes()); - // Write timestamp - data[0x0A..0x12].copy_from_slice(&self.timestamp.as_u64().to_le_bytes()); - // Write nonce - data[0x12..0x2A].copy_from_slice(&self.nonce); - // Write sender node id - data[0x2A..0x4A].copy_from_slice(&self.sender_id); - // Write recipient node id - data[0x4A..0x6A].copy_from_slice(&self.recipient_id); - - // Apply network key (not the best, but it will keep networks from colliding without much overhead) - if let Some(nk) = network_key.as_ref() { - let mut dh_secret_bytes = dh_secret.to_vec(); - - for n in 0..VLD0_SHARED_SECRET_LENGTH { - dh_secret_bytes[n] ^= nk[n]; - } - for n in 0..VLD0_NONCE_LENGTH { - data[0x12 + n] ^= nk[n]; - } - for n in 0..VLD0_HASH_DIGEST_LENGTH { - data[0x2A + n] ^= nk[n]; - } - for n in 0..VLD0_HASH_DIGEST_LENGTH { - data[0x4A + n] ^= nk[n]; - } - - dh_secret = BareSharedSecret::new(&dh_secret_bytes); - } - - // Encrypt message - let encrypted_body = vcrypto.crypt_no_auth_unaligned(&body, &self.nonce, &dh_secret)?; - - // Write body - if !encrypted_body.is_empty() { - data[0x6A..envelope_size - 64].copy_from_slice(encrypted_body.as_slice()); - } - - // Sign the envelope - let signature = vcrypto.sign( - &self.sender_id.clone().into(), - secret_key, - &data[0..(envelope_size - 64)], - )?; - - // Append the signature - data[(envelope_size - 64)..].copy_from_slice(&signature); - - Ok(data) - } - - pub fn get_version(&self) -> EnvelopeVersion { - self.version - } - - pub fn get_crypto_kind(&self) -> CryptoKind { - self.crypto_kind - } - - pub fn get_timestamp(&self) -> Timestamp { - self.timestamp - } - - #[expect(dead_code)] - pub fn get_nonce(&self) -> BareNonce { - self.nonce.clone() - } - - #[expect(dead_code)] - pub fn get_bare_sender_id(&self) -> BareNodeId { - self.sender_id.clone() - } - - pub fn get_sender_id(&self) -> NodeId { - NodeId::new(self.crypto_kind, self.sender_id.clone()) - } - - #[expect(dead_code)] - pub fn get_bare_recipient_id(&self) -> BareNodeId { - self.recipient_id.clone() - } - - pub fn get_recipient_id(&self) -> NodeId { - NodeId::new(self.crypto_kind, self.recipient_id.clone()) - } -} diff --git a/veilid-core/src/crypto/envelope/mod.rs b/veilid-core/src/crypto/envelope/mod.rs new file mode 100644 index 00000000..757098fd --- /dev/null +++ b/veilid-core/src/crypto/envelope/mod.rs @@ -0,0 +1,497 @@ +use super::*; +use crate::routing_table::*; +use core::convert::TryInto; + +// Version number of envelope format +fourcc_type!(EnvelopeVersion); + +// ENV0 +pub const ENVELOPE_VERSION_ENV0: EnvelopeVersion = EnvelopeVersion::new(*b"ENV0"); +pub const ENV0_NONCE_LENGTH: usize = 24; +pub const ENV0_SIGNATURE_LENGTH: usize = 64; +pub const ENV0_MAX_ENVELOPE_SIZE: usize = 65507; +pub const ENV0_MIN_ENVELOPE_SIZE: usize = 0x6A + 0x40; // Header + BareSignature + +/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one +pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [ENVELOPE_VERSION_ENV0]; +/// Number of envelope versions to keep on structures if many are present beyond the ones we consider valid +pub const MAX_ENVELOPE_VERSIONS: usize = 16; + +/// Envelopes are versioned +/// +/// These are the formats for the on-the-wire serialization performed by this module +/// +/// #[repr(C, packed)] +/// struct EnvelopeHeader { +/// // Size is at least 4 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct +/// version: [u8; 4], // 0x00: 0x45 0x4E 0x56 0x30 ("ENV0") +/// } +/// +/// #[repr(C, packed)] +/// struct EnvelopeENV0 { +/// // Size is 106 bytes without signature and 170 with signature +/// version: [u8; 4], // 0x00: 0x45 0x4E 0x56 0x30 ("ENV0") +/// crypto_kind: [u8; 4], // 0x04: CryptoSystemVersion FOURCC code (CryptoKind) +/// size: u16, // 0x08: Total size of the envelope including the encrypted operations message. Maximum size is 65,507 bytes, which is the data size limit for a single UDP message on IPv4. +/// timestamp: u64, // 0x0A: Duration since UNIX_EPOCH in microseconds when this message is sent. Messages older than 10 seconds are dropped. +/// nonce: [u8; 24], // 0x12: Random nonce for replay protection and for dh +/// sender_id: [u8; 32], // 0x2A: Node ID of the message source, which is the public key of the sender (must be verified with find_node if this is a new node_id/address combination) +/// recipient_id: [u8; 32], // 0x4A: Node ID of the intended recipient, which is the public key of the recipient (must be the receiving node, or a relay lease holder) +/// // 0x6A: message is appended (operations) +/// signature: [u8; 64], // 0x?? (end-0x40): BareSignature of the entire envelope including header is appended to the packet +/// // entire header needs to be included in message digest, relays are not allowed to modify the envelope without invalidating the signature. +/// } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Envelope { + ENV0 { env0: EnvelopeENV0 }, +} + +impl Envelope { + #[instrument(level = "trace", target = "envelope", skip_all)] + pub fn try_new_env0( + crypto: &Crypto, + crypto_kind: CryptoKind, + timestamp: Timestamp, + nonce: Nonce, + sender_id: NodeId, + recipient_id: NodeId, + ) -> VeilidAPIResult { + Ok(Self::ENV0 { + env0: EnvelopeENV0::try_new( + crypto, + crypto_kind, + timestamp, + nonce, + sender_id, + recipient_id, + )?, + }) + } + + #[instrument(level = "trace", target = "envelope", skip_all)] + pub fn try_from_signed_data( + crypto: &Crypto, + data: &[u8], + network_key: &Option, + ) -> VeilidAPIResult { + // Ensure we are at least the length of the envelope + // Silent drop here, as we use zero length packets as part of the protocol for hole punching + if data.len() < 4 { + apibail_generic!("envelope header too small"); + } + + // Check envelope version + let version: EnvelopeVersion = data[0x00..0x04] + .try_into() + .map_err(VeilidAPIError::internal)?; + + match version { + ENVELOPE_VERSION_ENV0 => Ok(Self::ENV0 { + env0: EnvelopeENV0::try_from_signed_data(crypto, data, network_key)?, + }), + _ => { + apibail_parse_error!("unsupported envelope version", version); + } + } + } + + #[instrument(level = "trace", target = "envelope", skip_all)] + pub fn decrypt_body( + &self, + crypto: &Crypto, + data: &[u8], + secret_key: &SecretKey, + network_key: &Option, + ) -> VeilidAPIResult> { + match self { + Envelope::ENV0 { env0 } => env0.decrypt_body(crypto, data, secret_key, network_key), + } + } + + #[instrument(level = "trace", target = "envelope", skip_all, err)] + pub fn to_encrypted_data( + &self, + crypto: &Crypto, + body: &[u8], + secret_key: &SecretKey, + network_key: &Option, + ) -> VeilidAPIResult> { + match self { + Envelope::ENV0 { env0 } => { + env0.to_encrypted_data(crypto, body, secret_key, network_key) + } + } + } + + pub fn get_version(&self) -> EnvelopeVersion { + match self { + Envelope::ENV0 { env0: _ } => ENVELOPE_VERSION_ENV0, + } + } + pub fn get_crypto_kind(&self) -> CryptoKind { + match self { + Envelope::ENV0 { env0 } => env0.get_crypto_kind(), + } + } + + pub fn get_timestamp(&self) -> Timestamp { + match self { + Envelope::ENV0 { env0 } => env0.get_timestamp(), + } + } + + pub fn get_sender_id(&self) -> NodeId { + match self { + Envelope::ENV0 { env0 } => env0.get_sender_id(), + } + } + + pub fn get_recipient_id(&self) -> NodeId { + match self { + Envelope::ENV0 { env0 } => env0.get_recipient_id(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EnvelopeENV0 { + crypto_kind: CryptoKind, + timestamp: Timestamp, + nonce: Nonce, + bare_sender_id: BareNodeId, + bare_recipient_id: BareNodeId, +} + +impl EnvelopeENV0 { + fn try_new( + crypto: &Crypto, + crypto_kind: CryptoKind, + timestamp: Timestamp, + nonce: Nonce, + sender_id: NodeId, + recipient_id: NodeId, + ) -> VeilidAPIResult { + let vcrypto = Self::validate_crypto_kind(crypto, crypto_kind)?; + + vcrypto.check_nonce(&nonce)?; + Self::check_node_id(crypto_kind, &sender_id)?; + Self::check_node_id(crypto_kind, &recipient_id)?; + + Ok(Self { + crypto_kind, + timestamp, + nonce, + bare_sender_id: sender_id.value(), + bare_recipient_id: recipient_id.value(), + }) + } + + fn try_from_signed_data( + crypto: &Crypto, + data: &[u8], + network_key: &Option, + ) -> VeilidAPIResult { + // Ensure we are at least the length of the envelope + // Silent drop here, as we use zero length packets as part of the protocol for hole punching + if data.len() < ENV0_MIN_ENVELOPE_SIZE { + apibail_generic!("envelope data too small"); + } + + // Check crypto kind + let crypto_kind = CryptoKind::new( + data[0x04..0x08] + .try_into() + .map_err(VeilidAPIError::internal)?, + ); + + let vcrypto = Self::validate_crypto_kind(crypto, crypto_kind)?; + + // Get size and ensure it matches the size of the envelope and is less than the maximum message size + let size: u16 = u16::from_le_bytes( + data[0x08..0x0A] + .try_into() + .map_err(VeilidAPIError::internal)?, + ); + if (size as usize) > ENV0_MAX_ENVELOPE_SIZE { + apibail_parse_error!("envelope too large", size); + } + if (size as usize) != data.len() { + apibail_parse_error!( + "size doesn't match envelope size", + format!( + "size doesn't match envelope size: size={} data.len()={}", + size, + data.len() + ) + ); + } + + // Get the timestamp + let timestamp: Timestamp = u64::from_le_bytes( + data[0x0A..0x12] + .try_into() + .map_err(VeilidAPIError::internal)?, + ) + .into(); + + // Get nonce and sender node id + let mut nonce_slice: [u8; ENV0_NONCE_LENGTH] = data[0x12..0x2A] + .try_into() + .map_err(VeilidAPIError::internal)?; + let mut sender_id_slice: [u8; HASH_COORDINATE_LENGTH] = data[0x2A..0x4A] + .try_into() + .map_err(VeilidAPIError::internal)?; + let mut recipient_id_slice: [u8; HASH_COORDINATE_LENGTH] = data[0x4A..0x6A] + .try_into() + .map_err(VeilidAPIError::internal)?; + + // Apply network key (not the best, but it will keep networks from colliding without much overhead) + if let Some(nk) = network_key.as_ref() { + for n in 0..ENV0_NONCE_LENGTH { + nonce_slice[n] ^= nk[n]; + } + for n in 0..HASH_COORDINATE_LENGTH { + sender_id_slice[n] ^= nk[n]; + } + for n in 0..HASH_COORDINATE_LENGTH { + recipient_id_slice[n] ^= nk[n]; + } + } + + let nonce: Nonce = Nonce::new(&nonce_slice); + let bare_sender_id = BareNodeId::new(&sender_id_slice); + let bare_recipient_id = BareNodeId::new(&recipient_id_slice); + + // Ensure sender_id and recipient_id are not the same + if bare_sender_id == bare_recipient_id { + apibail_parse_error!( + "bare_sender_id should not be same as bare_recipient_id", + bare_recipient_id.encode() + ); + } + + let sender_public_key = PublicKey::new(crypto_kind, BarePublicKey::new(&bare_sender_id)); + + // Get signature + let bare_signature = BareSignature::new( + data[(data.len() - ENV0_SIGNATURE_LENGTH)..] + .try_into() + .map_err(VeilidAPIError::internal)?, + ); + let signature = Signature::new(crypto_kind, bare_signature); + // Validate signature + if !vcrypto + .verify( + &sender_public_key, + &data[0..(data.len() - ENV0_SIGNATURE_LENGTH)], + &signature, + ) + .map_err(VeilidAPIError::internal)? + { + apibail_parse_error!("signature verification of envelope failed", signature); + } + + // Return envelope + Ok(Self { + crypto_kind, + timestamp, + nonce, + bare_sender_id, + bare_recipient_id, + }) + } + + pub fn decrypt_body( + &self, + crypto: &Crypto, + data: &[u8], + secret_key: &SecretKey, + network_key: &Option, + ) -> VeilidAPIResult> { + // Get DH secret + let vcrypto = crypto + .get(self.crypto_kind) + .expect("need to ensure only valid crypto kinds here"); + vcrypto.check_secret_key(secret_key)?; + + let sender_public_key = + PublicKey::new(self.crypto_kind, BarePublicKey::new(&self.bare_sender_id)); + + let mut dh_secret = vcrypto.cached_dh(&sender_public_key, secret_key)?; + + // Apply network key + if let Some(nk) = network_key.as_ref() { + let mut dh_secret_bytes = dh_secret.ref_value().to_vec(); + + for n in 0..dh_secret_bytes.len() { + dh_secret_bytes[n] ^= nk[n % dh_secret_bytes.len()]; + } + + dh_secret = + SharedSecret::new(dh_secret.kind(), BareSharedSecret::new(&dh_secret_bytes)); + } + // Decrypt message without authentication + let body = vcrypto.crypt_no_auth_aligned_8( + &data[0x6A..data.len() - ENV0_SIGNATURE_LENGTH], + &self.nonce, + &dh_secret, + )?; + + // Decompress body + let body = decompress_size_prepended(&body, Some(ENV0_MAX_ENVELOPE_SIZE))?; + + Ok(body) + } + + pub fn to_encrypted_data( + &self, + crypto: &Crypto, + body: &[u8], + secret_key: &SecretKey, + network_key: &Option, + ) -> VeilidAPIResult> { + let vcrypto = crypto + .get(self.crypto_kind) + .expect("need to ensure only valid crypto kinds here"); + vcrypto.check_secret_key(secret_key)?; + + // Ensure body isn't too long + let uncompressed_body_size: usize = body.len() + ENV0_MIN_ENVELOPE_SIZE; + if uncompressed_body_size > ENV0_MAX_ENVELOPE_SIZE { + apibail_parse_error!( + "envelope size before compression is too large", + uncompressed_body_size + ); + } + + // Compress body + let body = compress_prepend_size(body); + + // Ensure body isn't too long + let envelope_size: usize = body.len() + ENV0_MIN_ENVELOPE_SIZE; + if envelope_size > ENV0_MAX_ENVELOPE_SIZE { + apibail_parse_error!( + "envelope size after compression is too large", + envelope_size + ); + } + // Generate dh secret + let recipient_public_key = PublicKey::new( + self.crypto_kind, + BarePublicKey::new(&self.bare_recipient_id), + ); + + let mut dh_secret = vcrypto.cached_dh(&recipient_public_key, secret_key)?; + + // Write envelope body + let mut data = vec![0u8; envelope_size]; + + // Write version + data[0x00..0x04].copy_from_slice(&ENVELOPE_VERSION_ENV0.0); + // Write crypto kind + data[0x04..0x08].copy_from_slice(self.crypto_kind.bytes()); + // Write size + data[0x08..0x0A].copy_from_slice(&(envelope_size as u16).to_le_bytes()); + // Write timestamp + data[0x0A..0x12].copy_from_slice(&self.timestamp.as_u64().to_le_bytes()); + // Write nonce + data[0x12..0x2A].copy_from_slice(&self.nonce); + // Write sender node id + data[0x2A..0x4A].copy_from_slice(&self.bare_sender_id); + // Write recipient node id + data[0x4A..0x6A].copy_from_slice(&self.bare_recipient_id); + + // Apply network key (not the best, but it will keep networks from colliding without much overhead) + if let Some(nk) = network_key.as_ref() { + let mut dh_secret_bytes = dh_secret.ref_value().to_vec(); + + for n in 0..dh_secret_bytes.len() { + dh_secret_bytes[n] ^= nk[n % dh_secret_bytes.len()]; + } + for n in 0..ENV0_NONCE_LENGTH { + data[0x12 + n] ^= nk[n]; + } + for n in 0..HASH_COORDINATE_LENGTH { + data[0x2A + n] ^= nk[n]; + } + for n in 0..HASH_COORDINATE_LENGTH { + data[0x4A + n] ^= nk[n]; + } + + dh_secret = + SharedSecret::new(dh_secret.kind(), BareSharedSecret::new(&dh_secret_bytes)); + } + + // Encrypt message + let encrypted_body = vcrypto.crypt_no_auth_unaligned(&body, &self.nonce, &dh_secret)?; + + // Write body + if !encrypted_body.is_empty() { + data[0x6A..envelope_size - ENV0_SIGNATURE_LENGTH] + .copy_from_slice(encrypted_body.as_slice()); + } + + // Sign the envelope + let sender_public_key = + PublicKey::new(self.crypto_kind, BarePublicKey::new(&self.bare_sender_id)); + + let signature = vcrypto.sign( + &sender_public_key, + secret_key, + &data[0..(envelope_size - vcrypto.signature_length())], + )?; + + // Append the signature + data[(envelope_size - ENV0_SIGNATURE_LENGTH)..].copy_from_slice(signature.ref_value()); + + Ok(data) + } + + pub fn get_crypto_kind(&self) -> CryptoKind { + self.crypto_kind + } + + pub fn get_timestamp(&self) -> Timestamp { + self.timestamp + } + + pub fn get_sender_id(&self) -> NodeId { + NodeId::new(self.crypto_kind, self.bare_sender_id.clone()) + } + + pub fn get_recipient_id(&self) -> NodeId { + NodeId::new(self.crypto_kind, self.bare_recipient_id.clone()) + } + + ////////////////////////////////////////////////////////////////// + + fn validate_crypto_kind( + crypto: &Crypto, + crypto_kind: CryptoKind, + ) -> VeilidAPIResult> { + let vcrypto = crypto + .get(crypto_kind) + .ok_or_else(|| VeilidAPIError::parse_error("unsupported crypto kind", crypto_kind))?; + + // Verify crypto kind can be used with this envelope + if vcrypto.nonce_length() != ENV0_NONCE_LENGTH + || vcrypto.hash_digest_length() != HASH_COORDINATE_LENGTH + || vcrypto.public_key_length() != HASH_COORDINATE_LENGTH + || vcrypto.signature_length() != ENV0_SIGNATURE_LENGTH + { + apibail_generic!("unsupported crypto kind for this envelope type"); + } + + Ok(vcrypto) + } + + fn check_node_id(crypto_kind: CryptoKind, node_id: &NodeId) -> VeilidAPIResult<()> { + if node_id.kind() != crypto_kind { + apibail_parse_error!("invalid crypto kind for ENV0", node_id.kind()); + } + if node_id.ref_value().len() != HASH_COORDINATE_LENGTH { + apibail_parse_error!("invalid node_id length for ENV0", node_id.ref_value().len()); + } + Ok(()) + } +} diff --git a/veilid-core/src/crypto/guard.rs b/veilid-core/src/crypto/guard.rs index 2aecfa8a..9fc0f623 100644 --- a/veilid-core/src/crypto/guard.rs +++ b/veilid-core/src/crypto/guard.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use super::*; /// Guard to access a particular cryptosystem @@ -52,9 +54,9 @@ impl AsyncCryptoSystemGuard<'_> { // Cached Operations pub async fn cached_dh( &self, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult { + key: &PublicKey, + secret: &SecretKey, + ) -> VeilidAPIResult { yielding(|| self.guard.cached_dh(key, secret)).await } @@ -77,47 +79,50 @@ impl AsyncCryptoSystemGuard<'_> { &self, password: &[u8], salt: &[u8], - ) -> VeilidAPIResult { + ) -> VeilidAPIResult { yielding(|| self.guard.derive_shared_secret(password, salt)).await } - pub async fn random_nonce(&self) -> BareNonce { + pub async fn random_nonce(&self) -> Nonce { yielding(|| self.guard.random_nonce()).await } - pub async fn random_shared_secret(&self) -> BareSharedSecret { + pub async fn random_shared_secret(&self) -> SharedSecret { yielding(|| self.guard.random_shared_secret()).await } pub async fn compute_dh( &self, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult { + key: &PublicKey, + secret: &SecretKey, + ) -> VeilidAPIResult { yielding(|| self.guard.compute_dh(key, secret)).await } pub async fn generate_shared_secret( &self, - key: &BarePublicKey, - secret: &BareSecretKey, + key: &PublicKey, + secret: &SecretKey, domain: &[u8], - ) -> VeilidAPIResult { + ) -> VeilidAPIResult { let dh = self.compute_dh(key, secret).await?; - Ok(BareSharedSecret::from( - self.generate_hash(&[&dh, domain, VEILID_DOMAIN_API].concat()) - .await, + let hash = self + .generate_hash(&[&dh.into_value(), domain, VEILID_DOMAIN_API].concat()) + .await; + Ok(SharedSecret::new( + hash.kind(), + BareSharedSecret::new(&hash.into_value()), )) } - pub async fn generate_keypair(&self) -> BareKeyPair { + pub async fn generate_keypair(&self) -> KeyPair { yielding(|| self.guard.generate_keypair()).await } - pub async fn generate_hash(&self, data: &[u8]) -> BareHashDigest { + pub async fn generate_hash(&self, data: &[u8]) -> HashDigest { yielding(|| self.guard.generate_hash(data)).await } pub async fn generate_hash_reader( &self, reader: &mut dyn std::io::Read, - ) -> VeilidAPIResult { + ) -> VeilidAPIResult { yielding(|| self.guard.generate_hash_reader(reader)).await } @@ -154,59 +159,58 @@ impl AsyncCryptoSystemGuard<'_> { pub fn default_salt_length(&self) -> usize { self.guard.default_salt_length() } - pub fn check_shared_secret(&self, secret: &BareSharedSecret) -> VeilidAPIResult<()> { + pub fn check_shared_secret(&self, secret: &SharedSecret) -> VeilidAPIResult<()> { self.guard.check_shared_secret(secret) } - pub fn check_nonce(&self, nonce: &BareNonce) -> VeilidAPIResult<()> { + pub fn check_nonce(&self, nonce: &Nonce) -> VeilidAPIResult<()> { self.guard.check_nonce(nonce) } - pub fn check_hash_digest(&self, hash: &BareHashDigest) -> VeilidAPIResult<()> { + pub fn check_hash_digest(&self, hash: &HashDigest) -> VeilidAPIResult<()> { self.guard.check_hash_digest(hash) } - pub fn check_public_key(&self, key: &BarePublicKey) -> VeilidAPIResult<()> { + pub fn check_public_key(&self, key: &PublicKey) -> VeilidAPIResult<()> { self.guard.check_public_key(key) } - pub fn check_secret_key(&self, key: &BareSecretKey) -> VeilidAPIResult<()> { + pub fn check_secret_key(&self, key: &SecretKey) -> VeilidAPIResult<()> { self.guard.check_secret_key(key) } - pub fn check_signature(&self, signature: &BareSignature) -> VeilidAPIResult<()> { + pub fn check_signature(&self, signature: &Signature) -> VeilidAPIResult<()> { self.guard.check_signature(signature) } - pub async fn validate_keypair(&self, key: &BarePublicKey, secret: &BareSecretKey) -> bool { + pub async fn validate_keypair( + &self, + key: &PublicKey, + secret: &SecretKey, + ) -> VeilidAPIResult { yielding(|| self.guard.validate_keypair(key, secret)).await } - pub async fn validate_hash(&self, data: &[u8], hash: &BareHashDigest) -> bool { + pub async fn validate_hash(&self, data: &[u8], hash: &HashDigest) -> VeilidAPIResult { yielding(|| self.guard.validate_hash(data, hash)).await } pub async fn validate_hash_reader( &self, reader: &mut dyn std::io::Read, - hash: &BareHashDigest, + hash: &HashDigest, ) -> VeilidAPIResult { yielding(|| self.guard.validate_hash_reader(reader, hash)).await } - // Distance Metric - pub async fn distance(&self, key1: &BareHashDigest, key2: &BareHashDigest) -> BareHashDistance { - yielding(|| self.guard.distance(key1, key2)).await - } - // Authentication pub async fn sign( &self, - key: &BarePublicKey, - secret: &BareSecretKey, + key: &PublicKey, + secret: &SecretKey, data: &[u8], - ) -> VeilidAPIResult { + ) -> VeilidAPIResult { yielding(|| self.guard.sign(key, secret, data)).await } pub async fn verify( &self, - key: &BarePublicKey, + key: &PublicKey, data: &[u8], - signature: &BareSignature, + signature: &Signature, ) -> VeilidAPIResult { yielding(|| self.guard.verify(key, data, signature)).await } @@ -215,8 +219,8 @@ impl AsyncCryptoSystemGuard<'_> { pub async fn decrypt_in_place_aead( &self, body: &mut Vec, - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult<()> { yielding(|| { @@ -229,8 +233,8 @@ impl AsyncCryptoSystemGuard<'_> { pub async fn decrypt_aead( &self, body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult> { yielding(|| { @@ -243,8 +247,8 @@ impl AsyncCryptoSystemGuard<'_> { pub async fn encrypt_in_place_aead( &self, body: &mut Vec, - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult<()> { yielding(|| { @@ -257,8 +261,8 @@ impl AsyncCryptoSystemGuard<'_> { pub async fn encrypt_aead( &self, body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, associated_data: Option<&[u8]>, ) -> VeilidAPIResult> { yielding(|| { @@ -272,8 +276,8 @@ impl AsyncCryptoSystemGuard<'_> { pub async fn crypt_in_place_no_auth( &self, body: &mut [u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult<()> { yielding(|| { self.guard @@ -286,8 +290,8 @@ impl AsyncCryptoSystemGuard<'_> { &self, in_buf: &[u8], out_buf: &mut [u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult<()> { yielding(|| { self.guard @@ -299,8 +303,8 @@ impl AsyncCryptoSystemGuard<'_> { pub async fn crypt_no_auth_aligned_8( &self, body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult> { yielding(|| { self.guard @@ -312,8 +316,8 @@ impl AsyncCryptoSystemGuard<'_> { pub async fn crypt_no_auth_unaligned( &self, body: &[u8], - nonce: &BareNonce, - shared_secret: &BareSharedSecret, + nonce: &Nonce, + shared_secret: &SharedSecret, ) -> VeilidAPIResult> { yielding(|| { self.guard diff --git a/veilid-core/src/crypto/mod.rs b/veilid-core/src/crypto/mod.rs index 77293325..34a780b5 100644 --- a/veilid-core/src/crypto/mod.rs +++ b/veilid-core/src/crypto/mod.rs @@ -1,4 +1,3 @@ -mod blake3digest512; mod dh_cache; mod envelope; mod guard; @@ -6,41 +5,20 @@ mod receipt; mod types; pub mod crypto_system; -#[cfg(feature = "enable-crypto-none")] -pub(crate) mod none; - #[doc(hidden)] pub mod tests; -#[cfg(feature = "enable-crypto-vld0")] -pub(crate) mod vld0; -// #[cfg(feature = "enable-crypto-vld1")] -// pub(crate) mod vld1; - -pub use blake3digest512::*; pub use crypto_system::*; +use dh_cache::*; pub(crate) use envelope::*; pub use guard::*; pub(crate) use receipt::*; pub use types::*; -#[cfg(feature = "enable-crypto-none")] -pub use none::sizes::*; -#[cfg(feature = "enable-crypto-none")] -pub(crate) use none::*; -#[cfg(feature = "enable-crypto-vld0")] -pub use vld0::sizes::*; -#[cfg(feature = "enable-crypto-vld0")] -pub(crate) use vld0::*; -// #[cfg(feature = "enable-crypto-vld1")] -// pub(crate) use vld1::*; - use super::*; use core::convert::TryInto; -use dh_cache::*; use hashlink::linked_hash_map::Entry; use hashlink::LruCache; -use std::marker::PhantomData; impl_veilid_log_facility!("crypto"); @@ -67,8 +45,9 @@ cfg_if! { } /// Number of cryptosystem signatures to keep on structures if many are present beyond the ones we consider valid pub const MAX_CRYPTO_KINDS: usize = 3; + /// Return the best cryptosystem kind we support -pub fn best_crypto_kind() -> CryptoKind { +pub(crate) fn best_crypto_kind() -> CryptoKind { VALID_CRYPTO_KINDS[0] } @@ -160,16 +139,23 @@ impl Crypto { .open("crypto_caches", 1) .await .wrap_err("failed to open crypto_caches")?; - let caches_valid = match db.load(0, b"cache_validity_key").await? { + let mut caches_valid = match db.load(0, b"cache_validity_key").await? { Some(v) => v == cache_validity_key, None => false, }; + if caches_valid { if let Some(b) = db.load(0, b"dh_cache").await? { let mut inner = self.inner.lock(); - bytes_to_cache(&b, &mut inner.dh_cache); + if let Ok(dh_cache) = bytes_to_cache(&b) { + inner.dh_cache = dh_cache; + } else { + caches_valid = false; + } } - } else { + } + + if !caches_valid { drop(db); table_store.delete("crypto_caches").await?; db = table_store.open("crypto_caches", 1).await?; @@ -245,12 +231,12 @@ impl Crypto { } // Factory method to get the best crypto version - pub fn best(&self) -> CryptoSystemGuard<'_> { + pub(crate) fn best(&self) -> CryptoSystemGuard<'_> { self.get(best_crypto_kind()).unwrap() } // Factory method to get the best crypto version for async use - pub fn best_async(&self) -> AsyncCryptoSystemGuard<'_> { + pub(crate) fn best_async(&self) -> AsyncCryptoSystemGuard<'_> { self.get_async(best_crypto_kind()).unwrap() } @@ -261,17 +247,17 @@ impl Crypto { &self, public_keys: &[PublicKey], data: &[u8], - typed_signatures: &[Signature], + signatures: &[Signature], ) -> VeilidAPIResult> { let mut out = PublicKeyGroup::with_capacity(public_keys.len()); - for sig in typed_signatures { - for nid in public_keys { - if nid.kind() == sig.kind() { - if let Some(vcrypto) = self.get(sig.kind()) { - if !vcrypto.verify(nid.ref_value(), data, sig.ref_value())? { + for signature in signatures { + for public_key in public_keys { + if public_key.kind() == signature.kind() { + if let Some(vcrypto) = self.get(signature.kind()) { + if !vcrypto.verify(public_key, data, signature)? { return Ok(None); } - out.add(nid.clone()); + out.add(public_key.clone()); } } } @@ -285,17 +271,16 @@ impl Crypto { pub fn generate_signatures( &self, data: &[u8], - typed_key_pairs: &[KeyPair], + key_pairs: &[KeyPair], transform: F, ) -> VeilidAPIResult> where - F: Fn(&KeyPair, BareSignature) -> R, + F: Fn(&KeyPair, Signature) -> R, { - let mut out = Vec::::with_capacity(typed_key_pairs.len()); - for kp in typed_key_pairs { + let mut out = Vec::::with_capacity(key_pairs.len()); + for kp in key_pairs { if let Some(vcrypto) = self.get(kp.kind()) { - let sig = - vcrypto.sign(kp.ref_value().ref_key(), kp.ref_value().ref_secret(), data)?; + let sig = vcrypto.sign(&kp.key(), &kp.secret(), data)?; out.push(transform(kp, sig)) } } @@ -308,12 +293,12 @@ impl Crypto { #[cfg(feature = "enable-crypto-vld0")] if crypto_kind == CRYPTO_KIND_VLD0 { let kp = vld0_generate_keypair(); - return Ok(KeyPair::new(crypto_kind, kp)); + return Ok(kp); } #[cfg(feature = "enable-crypto-none")] if crypto_kind == CRYPTO_KIND_NONE { let kp = none_generate_keypair(); - return Ok(KeyPair::new(crypto_kind, kp)); + return Ok(kp); } Err(VeilidAPIError::generic("invalid crypto kind")) } @@ -323,9 +308,11 @@ impl Crypto { fn cached_dh_internal( &self, vcrypto: &T, - key: &BarePublicKey, - secret: &BareSecretKey, - ) -> VeilidAPIResult { + key: &PublicKey, + secret: &SecretKey, + ) -> VeilidAPIResult { + vcrypto.check_public_key(key)?; + vcrypto.check_secret_key(secret)?; Ok( match self.inner.lock().dh_cache.entry(DHCacheKey { key: key.clone(), @@ -430,10 +417,7 @@ impl Crypto { let (public_key, secret_key) = if let (Some(public_key), Some(secret_key)) = (public_key, secret_key) { // Validate node id - if !vcrypto - .validate_keypair(&public_key.value(), &secret_key.value()) - .await - { + if !vcrypto.validate_keypair(&public_key, &secret_key).await? { apibail_generic!(format!( "secret_key and public_key don't match:\npublic_key: {}\nsecret_key: {}", public_key, secret_key @@ -443,11 +427,7 @@ impl Crypto { } else { // If we still don't have a valid keypair, generate one veilid_log!(self debug "generating new node {} keypair", ck); - let kp = vcrypto.generate_keypair().await; - ( - PublicKey::new(ck, kp.key()), - SecretKey::new(ck, kp.secret()), - ) + vcrypto.generate_keypair().await.into_split() }; veilid_log!(self info "Public Key: {}", public_key); @@ -476,13 +456,7 @@ impl Crypto { .expect("Valid crypto kind is not actually valid."); #[cfg(test)] - let (public_key, secret_key) = { - let kp = vcrypto.generate_keypair().await; - ( - PublicKey::new(ck, kp.key()), - SecretKey::new(ck, kp.secret()), - ) - }; + let (public_key, secret_key) = vcrypto.generate_keypair().await.into_split(); #[cfg(not(test))] let (public_key, secret_key) = self.setup_public_key(vcrypto, table_store).await?; diff --git a/veilid-core/src/crypto/receipt.rs b/veilid-core/src/crypto/receipt.rs deleted file mode 100644 index 45ca7df2..00000000 --- a/veilid-core/src/crypto/receipt.rs +++ /dev/null @@ -1,238 +0,0 @@ -#![allow(clippy::absurd_extreme_comparisons)] - -use super::*; -use crate::*; -use core::convert::TryInto; - -pub const MAX_RECEIPT_SIZE: usize = 1380; -pub const MAX_EXTRA_DATA_SIZE: usize = MAX_RECEIPT_SIZE - MIN_RECEIPT_SIZE; // 1250 -pub const MIN_RECEIPT_SIZE: usize = 130; - -// Version number of receipt format -fourcc_type!(ReceiptVersion); - -pub const RECEIPT_VERSION_VLD0: ReceiptVersion = ReceiptVersion(*b"RCP0"); -// pub const RECEIPT_VERSION_VLD0_FOURCC: u32 = u32::from_be_bytes(*b"RCP0"); - -/// Receipt versions in order of preference, best receipt version is the first one, worst is the last one -pub const VALID_RECEIPT_VERSIONS: [ReceiptVersion; 1] = [RECEIPT_VERSION_VLD0]; - -static_assertions::const_assert_eq!(VALID_RECEIPT_VERSIONS.len(), VALID_ENVELOPE_VERSIONS.len()); - -/// Return the best receipt version we support -pub fn best_receipt_version() -> ReceiptVersion { - VALID_RECEIPT_VERSIONS[0] -} - -/// Out-of-band receipts are versioned along with envelopes. -/// -/// #[repr(C, packed)] -/// struct ReceiptHeader { -/// // Size is at least 4 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct -/// version: [u8; 4], // 0x00: 0x52 0x43 0x50 0x30 ("RCP0") -/// } -/// -/// #[repr(C, packed)] -/// struct ReceiptV0 { -/// // Size is 66 bytes without extra data and signature, 130 with signature -/// version: [u8; 4], // 0x00: 0x52 0x43 0x50 0x30 ("RCP0") -/// crypto_kind: [u8; 4], // 0x04: CryptoSystemVersion FOURCC code -/// size: u16, // 0x08: Total size of the receipt including the extra data and the signature. Maximum size is 1380 bytes. -/// nonce: [u8; 24], // 0x0A: Randomly chosen bytes that represent a unique receipt. Could be used to encrypt the extra data, but it's not required. -/// sender_id: [u8; 32], // 0x22: Node ID of the message source, which is the public key of the sender -/// extra_data: [u8; ??], // 0x42: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1250 bytes) -/// signature: [u8; 64], // 0x?? (end-0x40): BareSignature of the entire receipt including header and extra data is appended to the packet -/// } -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct Receipt { - version: ReceiptVersion, - crypto_kind: CryptoKind, - nonce: BareNonce, - sender_id: BareNodeId, - extra_data: Vec, -} - -impl Receipt { - pub fn try_new>( - version: ReceiptVersion, - crypto_kind: CryptoKind, - nonce: BareNonce, - sender_id: BareNodeId, - extra_data: D, - ) -> VeilidAPIResult { - assert!(VALID_RECEIPT_VERSIONS.contains(&version)); - assert!(VALID_CRYPTO_KINDS.contains(&crypto_kind)); - - if extra_data.as_ref().len() > MAX_EXTRA_DATA_SIZE { - apibail_parse_error!( - "extra data too large for receipt", - extra_data.as_ref().len() - ); - } - Ok(Self { - version, - crypto_kind, - nonce, - sender_id, - extra_data: Vec::from(extra_data.as_ref()), - }) - } - - #[instrument(level = "trace", target = "receipt", skip_all, err)] - pub fn from_signed_data(crypto: &Crypto, data: &[u8]) -> VeilidAPIResult { - // Ensure we are at least the length of the envelope - if data.len() < MIN_RECEIPT_SIZE { - apibail_parse_error!("receipt too small", data.len()); - } - - // Check version - let version: ReceiptVersion = data[0x00..0x04] - .try_into() - .map_err(VeilidAPIError::internal)?; - if !VALID_RECEIPT_VERSIONS.contains(&version) { - apibail_parse_error!("unsupported receipt version", version); - } - - // Check crypto kind - let crypto_kind = CryptoKind::try_from(&data[0x04..0x08])?; - let Some(vcrypto) = crypto.get(crypto_kind) else { - apibail_parse_error!("unsupported crypto kind", crypto_kind); - }; - - // Get size and ensure it matches the size of the envelope and is less than the maximum message size - let size: u16 = u16::from_le_bytes( - data[0x08..0x0A] - .try_into() - .map_err(VeilidAPIError::internal)?, - ); - if (size as usize) > MAX_RECEIPT_SIZE { - apibail_parse_error!("receipt size is too large", size); - } - if (size as usize) != data.len() { - apibail_parse_error!( - "size doesn't match receipt size", - format!("size={} data.len()={}", size, data.len()) - ); - } - - // Get sender id - let sender_id = BareNodeId::new( - data[0x22..0x42] - .try_into() - .map_err(VeilidAPIError::internal)?, - ); - - // Get signature - let signature = BareSignature::new( - data[(data.len() - 64)..] - .try_into() - .map_err(VeilidAPIError::internal)?, - ); - - // Validate signature - if !vcrypto - .verify( - &sender_id.clone().into(), - &data[0..(data.len() - 64)], - &signature, - ) - .map_err(VeilidAPIError::generic)? - { - apibail_parse_error!("signature failure in receipt", signature); - } - - // Get nonce - let nonce: BareNonce = BareNonce::new( - data[0x0A..0x22] - .try_into() - .map_err(VeilidAPIError::internal)?, - ); - - // Get extra data and signature - let extra_data: Vec = Vec::from(&data[0x42..(data.len() - 64)]); - - // Return receipt - Ok(Self { - version, - crypto_kind, - nonce, - sender_id, - extra_data, - }) - } - - #[instrument(level = "trace", target = "receipt", skip_all, err)] - pub fn to_signed_data( - &self, - crypto: &Crypto, - secret: &BareSecretKey, - ) -> VeilidAPIResult> { - // Ensure extra data isn't too long - let receipt_size: usize = self.extra_data.len() + MIN_RECEIPT_SIZE; - if receipt_size > MAX_RECEIPT_SIZE { - apibail_parse_error!("receipt too large", receipt_size); - } - // Get crypto version - let vcrypto = crypto - .get(self.crypto_kind) - .expect("need to ensure only valid crypto kinds here"); - - let mut data: Vec = vec![0u8; receipt_size]; - - // Write version - data[0x00..0x04].copy_from_slice(&self.version.0); - // Write crypto kind - data[0x04..0x08].copy_from_slice(&self.crypto_kind.0); - // Write size - data[0x08..0x0A].copy_from_slice(&(receipt_size as u16).to_le_bytes()); - // Write nonce - data[0x0A..0x22].copy_from_slice(&self.nonce); - // Write sender node id - data[0x22..0x42].copy_from_slice(&self.sender_id); - // Write extra data - if !self.extra_data.is_empty() { - data[0x42..(receipt_size - 64)].copy_from_slice(self.extra_data.as_slice()); - } - // Sign the receipt - let signature = vcrypto - .sign( - &self.sender_id.clone().into(), - secret, - &data[0..(receipt_size - 64)], - ) - .map_err(VeilidAPIError::generic)?; - // Append the signature - data[(receipt_size - 64)..].copy_from_slice(&signature); - - Ok(data) - } - - #[expect(dead_code)] - pub fn get_version(&self) -> ReceiptVersion { - self.version - } - - #[expect(dead_code)] - pub fn get_crypto_kind(&self) -> CryptoKind { - self.crypto_kind - } - - pub fn get_nonce(&self) -> BareNonce { - self.nonce.clone() - } - - #[expect(dead_code)] - pub fn get_bare_sender_id(&self) -> BareNodeId { - self.sender_id.clone() - } - - #[expect(dead_code)] - pub fn get_sender_id(&self) -> NodeId { - NodeId::new(self.crypto_kind, self.sender_id.clone()) - } - - #[must_use] - pub fn get_extra_data(&self) -> &[u8] { - &self.extra_data - } -} diff --git a/veilid-core/src/crypto/receipt/mod.rs b/veilid-core/src/crypto/receipt/mod.rs new file mode 100644 index 00000000..7bd7bee1 --- /dev/null +++ b/veilid-core/src/crypto/receipt/mod.rs @@ -0,0 +1,337 @@ +#![allow(clippy::absurd_extreme_comparisons)] + +use super::*; +use crate::routing_table::*; +use core::convert::TryInto; + +// Version number of receipt format +fourcc_type!(ReceiptVersion); + +// RCP0 +pub const RECEIPT_VERSION_RCP0: ReceiptVersion = ReceiptVersion::new(*b"RCP0"); +pub const RCP0_NONCE_LENGTH: usize = 24; +pub const RCP0_SIGNATURE_LENGTH: usize = 64; +pub const RCP0_MAX_RECEIPT_SIZE: usize = 1380; +pub const RCP0_MAX_EXTRA_DATA_SIZE: usize = RCP0_MAX_RECEIPT_SIZE - RCP0_MIN_RECEIPT_SIZE; // 1250 +pub const RCP0_MIN_RECEIPT_SIZE: usize = 130; + +/// Receipt versions in order of preference, best receipt version is the first one, worst is the last one +pub const VALID_RECEIPT_VERSIONS: [ReceiptVersion; 1] = [RECEIPT_VERSION_RCP0]; + +static_assertions::const_assert_eq!(VALID_RECEIPT_VERSIONS.len(), VALID_ENVELOPE_VERSIONS.len()); + +/// Return the best receipt version we support +pub fn best_receipt_version() -> ReceiptVersion { + VALID_RECEIPT_VERSIONS[0] +} + +/// Out-of-band receipts are versioned along with envelopes. +/// +/// #[repr(C, packed)] +/// struct ReceiptHeader { +/// // Size is at least 4 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct +/// version: [u8; 4], // 0x00: 0x52 0x43 0x50 0x30 ("RCP0") +/// } +/// +/// #[repr(C, packed)] +/// struct ReceiptRCP0 { +/// // Size is 66 bytes without extra data and signature, 130 with signature +/// version: [u8; 4], // 0x00: 0x52 0x43 0x50 0x30 ("RCP0") +/// crypto_kind: [u8; 4], // 0x04: CryptoSystemVersion FOURCC code +/// size: u16, // 0x08: Total size of the receipt including the extra data and the signature. Maximum size is 1380 bytes. +/// nonce: [u8; 24], // 0x0A: Randomly chosen bytes that represent a unique receipt. Could be used to encrypt the extra data, but it's not required. +/// sender_id: [u8; 32], // 0x22: Node ID of the message source, which is the public key of the sender +/// extra_data: [u8; ??], // 0x42: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1250 bytes) +/// signature: [u8; 64], // 0x?? (end-0x40): BareSignature of the entire receipt including header and extra data is appended to the packet +/// } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Receipt { + RCP0 { rcp0: ReceiptRCP0 }, +} + +impl Receipt { + #[instrument(level = "trace", target = "envelope", skip_all)] + pub fn try_new_rcp0>( + crypto: &Crypto, + crypto_kind: CryptoKind, + nonce: Nonce, + sender_id: NodeId, + extra_data: D, + ) -> VeilidAPIResult { + Ok(Self::RCP0 { + rcp0: ReceiptRCP0::try_new(crypto, crypto_kind, nonce, sender_id, extra_data)?, + }) + } + + #[instrument(level = "trace", target = "receipt", skip_all, err)] + pub fn try_from_signed_data(crypto: &Crypto, data: &[u8]) -> VeilidAPIResult { + // Ensure we are at least the length of the envelope + if data.len() < 4 { + apibail_parse_error!("receipt header too small", data.len()); + } + + // Check version + let version: ReceiptVersion = data[0x00..0x04] + .try_into() + .map_err(VeilidAPIError::internal)?; + + match version { + RECEIPT_VERSION_RCP0 => Ok(Self::RCP0 { + rcp0: ReceiptRCP0::try_from_signed_data(crypto, data)?, + }), + _ => { + apibail_parse_error!("unsupported receipt version", version); + } + } + } + + #[instrument(level = "trace", target = "envelope", skip_all)] + pub fn to_signed_data( + &self, + crypto: &Crypto, + secret_key: &SecretKey, + ) -> VeilidAPIResult> { + match self { + Receipt::RCP0 { rcp0 } => rcp0.to_signed_data(crypto, secret_key), + } + } + + #[expect(dead_code)] + pub fn get_version(&self) -> ReceiptVersion { + match self { + Receipt::RCP0 { rcp0: _ } => RECEIPT_VERSION_RCP0, + } + } + + #[expect(dead_code)] + pub fn get_crypto_kind(&self) -> CryptoKind { + match self { + Receipt::RCP0 { rcp0 } => rcp0.get_crypto_kind(), + } + } + + pub fn get_nonce(&self) -> Nonce { + match self { + Receipt::RCP0 { rcp0 } => rcp0.get_nonce(), + } + } + + #[expect(dead_code)] + pub fn get_sender_id(&self) -> NodeId { + match self { + Receipt::RCP0 { rcp0 } => rcp0.get_sender_id(), + } + } + + pub fn get_extra_data(&self) -> &[u8] { + match self { + Receipt::RCP0 { rcp0 } => rcp0.get_extra_data(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ReceiptRCP0 { + crypto_kind: CryptoKind, + nonce: Nonce, + bare_sender_id: BareNodeId, + extra_data: Vec, +} + +impl ReceiptRCP0 { + pub fn try_new>( + crypto: &Crypto, + crypto_kind: CryptoKind, + nonce: Nonce, + sender_id: NodeId, + extra_data: D, + ) -> VeilidAPIResult { + let vcrypto = Self::validate_crypto_kind(crypto, crypto_kind)?; + + vcrypto.check_nonce(&nonce)?; + Self::check_node_id(crypto_kind, &sender_id)?; + + if extra_data.as_ref().len() > RCP0_MAX_EXTRA_DATA_SIZE { + apibail_parse_error!( + "extra data too large for receipt", + extra_data.as_ref().len() + ); + } + + Ok(Self { + crypto_kind, + nonce, + bare_sender_id: sender_id.value(), + extra_data: Vec::from(extra_data.as_ref()), + }) + } + + #[instrument(level = "trace", target = "receipt", skip_all, err)] + pub fn try_from_signed_data(crypto: &Crypto, data: &[u8]) -> VeilidAPIResult { + // Ensure we are at least the length of the envelope + if data.len() < RCP0_MIN_RECEIPT_SIZE { + apibail_parse_error!("receipt too small", data.len()); + } + + // Check crypto kind + let crypto_kind = CryptoKind::try_from(&data[0x04..0x08])?; + let vcrypto = Self::validate_crypto_kind(crypto, crypto_kind)?; + + // Get size and ensure it matches the size of the envelope and is less than the maximum message size + let size: u16 = u16::from_le_bytes( + data[0x08..0x0A] + .try_into() + .map_err(VeilidAPIError::internal)?, + ); + if (size as usize) > RCP0_MAX_RECEIPT_SIZE { + apibail_parse_error!("receipt size is too large", size); + } + if (size as usize) != data.len() { + apibail_parse_error!( + "size doesn't match receipt size", + format!("size={} data.len()={}", size, data.len()) + ); + } + + // Get sender id + let bare_sender_id = BareNodeId::new( + data[0x22..0x42] + .try_into() + .map_err(VeilidAPIError::internal)?, + ); + let sender_public_key = PublicKey::new(crypto_kind, BarePublicKey::new(&bare_sender_id)); + + // Get signature + let bare_signature = BareSignature::new( + data[(data.len() - 64)..] + .try_into() + .map_err(VeilidAPIError::internal)?, + ); + + let signature = Signature::new(crypto_kind, bare_signature); + + // Validate signature + if !vcrypto + .verify(&sender_public_key, &data[0..(data.len() - 64)], &signature) + .map_err(VeilidAPIError::generic)? + { + apibail_parse_error!("signature failure in receipt", signature); + } + + // Get nonce + let nonce: Nonce = Nonce::new( + data[0x0A..0x22] + .try_into() + .map_err(VeilidAPIError::internal)?, + ); + + // Get extra data and signature + let extra_data: Vec = Vec::from(&data[0x42..(data.len() - 64)]); + + // Return receipt + Ok(Self { + crypto_kind, + nonce, + bare_sender_id, + extra_data, + }) + } + + #[instrument(level = "trace", target = "receipt", skip_all, err)] + pub fn to_signed_data( + &self, + crypto: &Crypto, + secret_key: &SecretKey, + ) -> VeilidAPIResult> { + let vcrypto = crypto + .get(self.crypto_kind) + .expect("need to ensure only valid crypto kinds here"); + vcrypto.check_secret_key(secret_key)?; + + // Ensure extra data isn't too long + let receipt_size: usize = self.extra_data.len() + RCP0_MIN_RECEIPT_SIZE; + if receipt_size > RCP0_MAX_RECEIPT_SIZE { + apibail_parse_error!("receipt too large", receipt_size); + } + + let mut data: Vec = vec![0u8; receipt_size]; + + // Write version + data[0x00..0x04].copy_from_slice(&RECEIPT_VERSION_RCP0.0); + // Write crypto kind + data[0x04..0x08].copy_from_slice(self.crypto_kind.bytes()); + // Write size + data[0x08..0x0A].copy_from_slice(&(receipt_size as u16).to_le_bytes()); + // Write nonce + data[0x0A..0x22].copy_from_slice(&self.nonce); + // Write sender node id + data[0x22..0x42].copy_from_slice(&self.bare_sender_id); + // Write extra data + if !self.extra_data.is_empty() { + data[0x42..(receipt_size - RCP0_SIGNATURE_LENGTH)] + .copy_from_slice(self.extra_data.as_slice()); + } + // Sign the receipt + let sender_public_key = + PublicKey::new(self.crypto_kind, BarePublicKey::new(&self.bare_sender_id)); + let signature = vcrypto + .sign( + &sender_public_key, + secret_key, + &data[0..(receipt_size - RCP0_SIGNATURE_LENGTH)], + ) + .map_err(VeilidAPIError::generic)?; + // Append the signature + data[(receipt_size - 64)..].copy_from_slice(signature.ref_value()); + + Ok(data) + } + + pub fn get_crypto_kind(&self) -> CryptoKind { + self.crypto_kind + } + + pub fn get_nonce(&self) -> Nonce { + self.nonce.clone() + } + + pub fn get_sender_id(&self) -> NodeId { + NodeId::new(self.crypto_kind, self.bare_sender_id.clone()) + } + + pub fn get_extra_data(&self) -> &[u8] { + &self.extra_data + } + + ////////////////////////////////////////////////////////////////// + + fn validate_crypto_kind( + crypto: &Crypto, + crypto_kind: CryptoKind, + ) -> VeilidAPIResult> { + let vcrypto = crypto + .get(crypto_kind) + .ok_or_else(|| VeilidAPIError::parse_error("unsupported crypto kind", crypto_kind))?; + + // Verify crypto kind can be used with this envelope + if vcrypto.nonce_length() != RCP0_NONCE_LENGTH + || vcrypto.hash_digest_length() != HASH_COORDINATE_LENGTH + || vcrypto.public_key_length() != HASH_COORDINATE_LENGTH + { + apibail_generic!("unsupported crypto kind for this envelope type"); + } + + Ok(vcrypto) + } + + fn check_node_id(crypto_kind: CryptoKind, node_id: &NodeId) -> VeilidAPIResult<()> { + if node_id.kind() != crypto_kind { + apibail_parse_error!("invalid crypto kind for RCP0", node_id.kind()); + } + if node_id.ref_value().len() != HASH_COORDINATE_LENGTH { + apibail_parse_error!("invalid node_id length for RCP0", node_id.ref_value().len()); + } + Ok(()) + } +} diff --git a/veilid-core/src/crypto/tests/fixtures.rs b/veilid-core/src/crypto/tests/fixtures.rs index f7316b27..74dbdbcd 100644 --- a/veilid-core/src/crypto/tests/fixtures.rs +++ b/veilid-core/src/crypto/tests/fixtures.rs @@ -132,6 +132,13 @@ pub fn fix_fake_public_key() -> PublicKey { ) } +pub fn fix_fake_secret_key() -> SecretKey { + SecretKey::new( + CryptoKind::from_str("FAKE").unwrap(), + fix_fake_bare_secret_key(), + ) +} + pub fn fix_fake_bare_secret_key() -> BareSecretKey { let mut fake_key = [0u8; VLD0_SECRET_KEY_LENGTH]; random_bytes(&mut fake_key); diff --git a/veilid-core/src/crypto/tests/test_crypto.rs b/veilid-core/src/crypto/tests/test_crypto.rs index 086ef538..05a9ca67 100644 --- a/veilid-core/src/crypto/tests/test_crypto.rs +++ b/veilid-core/src/crypto/tests/test_crypto.rs @@ -182,9 +182,15 @@ pub async fn test_no_auth(vcrypto: &AsyncCryptoSystemGuard<'_>) { pub async fn test_dh(vcrypto: &AsyncCryptoSystemGuard<'_>) { trace!("test_dh"); let (dht_key, dht_key_secret) = vcrypto.generate_keypair().await.into_split(); - assert!(vcrypto.validate_keypair(&dht_key, &dht_key_secret).await); + assert!(vcrypto + .validate_keypair(&dht_key, &dht_key_secret) + .await + .expect("should succeed")); let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split(); - assert!(vcrypto.validate_keypair(&dht_key2, &dht_key_secret2).await); + assert!(vcrypto + .validate_keypair(&dht_key2, &dht_key_secret2) + .await + .expect("should succeed")); let r1 = vcrypto .compute_dh(&dht_key, &dht_key_secret2) diff --git a/veilid-core/src/crypto/tests/test_envelope_receipt.rs b/veilid-core/src/crypto/tests/test_envelope_receipt.rs index 80b81407..54ab8ebd 100644 --- a/veilid-core/src/crypto/tests/test_envelope_receipt.rs +++ b/veilid-core/src/crypto/tests/test_envelope_receipt.rs @@ -18,16 +18,25 @@ pub async fn test_envelope_round_trip( // Create envelope let ts = Timestamp::from(0x12345678ABCDEF69u64); let nonce = vcrypto.random_nonce().await; - let (sender_id, sender_secret) = vcrypto.generate_keypair().await.into_split(); - let (recipient_id, recipient_secret) = vcrypto.generate_keypair().await.into_split(); - let envelope = Envelope::new( - envelope_version, - vcrypto.kind(), - ts, - nonce, - sender_id.into(), - recipient_id.into(), - ); + let (sender_public_key, sender_secret) = vcrypto.generate_keypair().await.into_split(); + let sender_id = crypto + .routing_table() + .generate_node_id(&sender_public_key) + .expect("should generate node id"); + let (recipient_public_key, recipient_secret) = vcrypto.generate_keypair().await.into_split(); + let recipient_id = crypto + .routing_table() + .generate_node_id(&recipient_public_key) + .expect("should generate node id"); + let envelope = match envelope_version { + ENVELOPE_VERSION_ENV0 => { + Envelope::try_new_env0(&crypto, vcrypto.kind(), ts, nonce, sender_id, recipient_id) + .expect("should create envelope") + } + _ => { + panic!("unsupported envelope version"); + } + }; // Create arbitrary body let body = b"This is an arbitrary body"; @@ -38,7 +47,7 @@ pub async fn test_envelope_round_trip( .expect("failed to encrypt data"); // Deserialize from bytes - let envelope2 = Envelope::from_signed_data(&crypto, &enc_data, &network_key) + let envelope2 = Envelope::try_from_signed_data(&crypto, &enc_data, &network_key) .expect("failed to deserialize envelope from data"); let body2 = envelope2 @@ -54,13 +63,13 @@ pub async fn test_envelope_round_trip( let mut mod_enc_data = enc_data.clone(); mod_enc_data[enc_data_len - 1] ^= 0x80u8; assert!( - Envelope::from_signed_data(&crypto, &mod_enc_data, &network_key).is_err(), + Envelope::try_from_signed_data(&crypto, &mod_enc_data, &network_key).is_err(), "should have failed to decode envelope with modified signature" ); let mut mod_enc_data2 = enc_data.clone(); mod_enc_data2[enc_data_len - 65] ^= 0x80u8; assert!( - Envelope::from_signed_data(&crypto, &mod_enc_data2, &network_key).is_err(), + Envelope::try_from_signed_data(&crypto, &mod_enc_data2, &network_key).is_err(), "should have failed to decode envelope with modified data" ); } @@ -76,15 +85,20 @@ pub async fn test_receipt_round_trip( // Create receipt let nonce = vcrypto.random_nonce().await; - let (sender_id, sender_secret) = vcrypto.generate_keypair().await.into_split(); - let receipt = Receipt::try_new( - receipt_version, - vcrypto.kind(), - nonce, - sender_id.into(), - body, - ) - .expect("should not fail"); + let (sender_public_key, sender_secret) = vcrypto.generate_keypair().await.into_split(); + let sender_id = crypto + .routing_table() + .generate_node_id(&sender_public_key) + .expect("should generate node id"); + let receipt = match receipt_version { + RECEIPT_VERSION_RCP0 => { + Receipt::try_new_rcp0(&crypto, vcrypto.kind(), nonce, sender_id, body) + .expect("should not fail") + } + _ => { + panic!("unsupported receipt version"); + } + }; // Serialize to bytes let mut enc_data = receipt @@ -92,12 +106,12 @@ pub async fn test_receipt_round_trip( .expect("failed to make signed data"); // Deserialize from bytes - let receipt2 = Receipt::from_signed_data(&crypto, &enc_data) + let receipt2 = Receipt::try_from_signed_data(&crypto, &enc_data) .expect("failed to deserialize envelope from data"); // Should not validate even when a single bit is changed enc_data[5] = 0x01; - let _ = Receipt::from_signed_data(&crypto, &enc_data) + let _ = Receipt::try_from_signed_data(&crypto, &enc_data) .expect_err("should have failed to decrypt using wrong secret"); // Compare receipts @@ -114,8 +128,12 @@ pub async fn test_all() { let vcrypto = crypto.get_async(v).unwrap(); test_envelope_round_trip(ev, &vcrypto, None).await; - test_envelope_round_trip(ev, &vcrypto, Some(vcrypto.random_shared_secret().await)) - .await; + test_envelope_round_trip( + ev, + &vcrypto, + Some(vcrypto.random_shared_secret().await.into_value()), + ) + .await; } } diff --git a/veilid-core/src/crypto/tests/test_types.rs b/veilid-core/src/crypto/tests/test_types.rs index 835d8ecf..244def9c 100644 --- a/veilid-core/src/crypto/tests/test_types.rs +++ b/veilid-core/src/crypto/tests/test_types.rs @@ -3,162 +3,188 @@ use crate::crypto::tests::fixtures::*; pub async fn test_generate_secret(vcrypto: &AsyncCryptoSystemGuard<'_>) { // Verify keys generate - let (dht_key, dht_key_secret) = vcrypto.generate_keypair().await.into_split(); - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split(); + let (public_key, secret_key) = vcrypto.generate_keypair().await.into_split(); + let (public_key2, secret_key2) = vcrypto.generate_keypair().await.into_split(); // Verify byte patterns are different between public and secret - assert_ne!(dht_key.bytes(), dht_key_secret.bytes()); - assert_ne!(dht_key2.bytes(), dht_key_secret2.bytes()); + assert_ne!( + public_key.ref_value().bytes(), + secret_key.ref_value().bytes() + ); + assert_ne!( + public_key2.ref_value().bytes(), + secret_key2.ref_value().bytes() + ); // Verify the keys and secrets are different across keypairs - assert_ne!(dht_key, dht_key2); - assert_ne!(dht_key_secret, dht_key_secret2); + assert_ne!(public_key, public_key2); + assert_ne!(secret_key, secret_key2); } pub async fn test_sign_and_verify(vcrypto: &AsyncCryptoSystemGuard<'_>) { // Make two keys - let (dht_key, dht_key_secret) = vcrypto.generate_keypair().await.into_split(); - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split(); + let (public_key, secret_key) = vcrypto.generate_keypair().await.into_split(); + let (public_key2, secret_key2) = vcrypto.generate_keypair().await.into_split(); // Sign the same message twice - let dht_sig = vcrypto - .sign(&dht_key, &dht_key_secret, LOREM_IPSUM) + let sig = vcrypto + .sign(&public_key, &secret_key, LOREM_IPSUM) .await .unwrap(); - trace!("dht_sig: {:?}", dht_sig); - let dht_sig_b = vcrypto - .sign(&dht_key, &dht_key_secret, LOREM_IPSUM) + trace!("sig: {:?}", sig); + let sig_b = vcrypto + .sign(&public_key, &secret_key, LOREM_IPSUM) .await .unwrap(); // Sign a second message - let dht_sig_c = vcrypto - .sign(&dht_key, &dht_key_secret, CHEEZBURGER) + let sig_c = vcrypto + .sign(&public_key, &secret_key, CHEEZBURGER) .await .unwrap(); - trace!("dht_sig_c: {:?}", dht_sig_c); + trace!("sig_c: {:?}", sig_c); // Verify they are the same signature - assert_eq!(dht_sig, dht_sig_b); + assert_eq!(sig, sig_b); // Sign the same message with a different key - let dht_sig2 = vcrypto - .sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM) + let sig2 = vcrypto + .sign(&public_key2, &secret_key2, LOREM_IPSUM) .await .unwrap(); // Verify a different key gives a different signature - assert_ne!(dht_sig2, dht_sig_b); + assert_ne!(sig2, sig_b); // Try using the wrong secret to sign let a1 = vcrypto - .sign(&dht_key, &dht_key_secret, LOREM_IPSUM) + .sign(&public_key, &secret_key, LOREM_IPSUM) .await .unwrap(); let a2 = vcrypto - .sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM) + .sign(&public_key2, &secret_key2, LOREM_IPSUM) .await .unwrap(); let _b1 = vcrypto - .sign(&dht_key, &dht_key_secret2, LOREM_IPSUM) + .sign(&public_key, &secret_key2, LOREM_IPSUM) .await .unwrap_err(); let _b2 = vcrypto - .sign(&dht_key2, &dht_key_secret, LOREM_IPSUM) + .sign(&public_key2, &secret_key, LOREM_IPSUM) .await .unwrap_err(); assert_ne!(a1, a2); - assert_eq!(vcrypto.verify(&dht_key, LOREM_IPSUM, &a1).await, Ok(true)); - assert_eq!(vcrypto.verify(&dht_key2, LOREM_IPSUM, &a2).await, Ok(true)); - assert_eq!(vcrypto.verify(&dht_key, LOREM_IPSUM, &a2).await, Ok(false)); - assert_eq!(vcrypto.verify(&dht_key2, LOREM_IPSUM, &a1).await, Ok(false)); + assert_eq!( + vcrypto.verify(&public_key, LOREM_IPSUM, &a1).await, + Ok(true) + ); + assert_eq!( + vcrypto.verify(&public_key2, LOREM_IPSUM, &a2).await, + Ok(true) + ); + assert_eq!( + vcrypto.verify(&public_key, LOREM_IPSUM, &a2).await, + Ok(false) + ); + assert_eq!( + vcrypto.verify(&public_key2, LOREM_IPSUM, &a1).await, + Ok(false) + ); // Try verifications that should work assert_eq!( - vcrypto.verify(&dht_key, LOREM_IPSUM, &dht_sig).await, + vcrypto.verify(&public_key, LOREM_IPSUM, &sig).await, Ok(true) ); assert_eq!( - vcrypto.verify(&dht_key, LOREM_IPSUM, &dht_sig_b).await, + vcrypto.verify(&public_key, LOREM_IPSUM, &sig_b).await, Ok(true) ); assert_eq!( - vcrypto.verify(&dht_key2, LOREM_IPSUM, &dht_sig2).await, + vcrypto.verify(&public_key2, LOREM_IPSUM, &sig2).await, Ok(true) ); assert_eq!( - vcrypto.verify(&dht_key, CHEEZBURGER, &dht_sig_c).await, + vcrypto.verify(&public_key, CHEEZBURGER, &sig_c).await, Ok(true) ); // Try verifications that shouldn't work assert_eq!( - vcrypto.verify(&dht_key2, LOREM_IPSUM, &dht_sig).await, + vcrypto.verify(&public_key2, LOREM_IPSUM, &sig).await, Ok(false) ); assert_eq!( - vcrypto.verify(&dht_key, LOREM_IPSUM, &dht_sig2).await, + vcrypto.verify(&public_key, LOREM_IPSUM, &sig2).await, Ok(false) ); assert_eq!( - vcrypto.verify(&dht_key2, CHEEZBURGER, &dht_sig_c).await, + vcrypto.verify(&public_key2, CHEEZBURGER, &sig_c).await, Ok(false) ); assert_eq!( - vcrypto.verify(&dht_key, CHEEZBURGER, &dht_sig).await, + vcrypto.verify(&public_key, CHEEZBURGER, &sig).await, Ok(false) ); } pub async fn test_key_conversions(vcrypto: &AsyncCryptoSystemGuard<'_>) { // Test default key - let (dht_key, dht_key_secret) = (BarePublicKey::default(), BareSecretKey::default()); - assert!(dht_key.bytes().is_empty()); - assert!(dht_key_secret.bytes().is_empty()); - let dht_key_string = String::from(&dht_key); - trace!("dht_key_string: {:?}", dht_key_string); - let dht_key_string2 = String::from(&dht_key); - trace!("dht_key_string2: {:?}", dht_key_string2); - assert_eq!(dht_key_string, dht_key_string2); + let (public_key, secret_key) = ( + PublicKey::new( + vcrypto.kind(), + BarePublicKey::new(&vec![0u8; vcrypto.public_key_length()]), + ), + SecretKey::new( + vcrypto.kind(), + BareSecretKey::new(&vec![0u8; vcrypto.secret_key_length()]), + ), + ); + let public_key_string = public_key.to_string(); + trace!("public_key_string: {:?}", public_key_string); + let public_key_string2 = public_key.to_string(); + trace!("public_key_string2: {:?}", public_key_string2); + assert_eq!(public_key_string, public_key_string2); - let dht_key_secret_string = String::from(&dht_key_secret); - trace!("dht_key_secret_string: {:?}", dht_key_secret_string); - assert_eq!(dht_key_secret_string, dht_key_string); + let secret_key_string = secret_key.to_string(); + trace!("secret_key_string: {:?}", secret_key_string); + assert_eq!(secret_key_string, public_key_string); // Make different keys - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split(); - trace!("dht_key2: {:?}", dht_key2); - trace!("dht_key_secret2: {:?}", dht_key_secret2); - let (dht_key3, _dht_key_secret3) = vcrypto.generate_keypair().await.into_split(); - trace!("dht_key3: {:?}", dht_key3); - trace!("_dht_key_secret3: {:?}", _dht_key_secret3); + let (public_key2, secret_key2) = vcrypto.generate_keypair().await.into_split(); + trace!("public_key2: {:?}", public_key2); + trace!("secret_key2: {:?}", secret_key2); + let (public_key3, secret_key3) = vcrypto.generate_keypair().await.into_split(); + trace!("public_key3: {:?}", public_key3); + trace!("secret_key3: {:?}", secret_key3); - let dht_key2_string = String::from(&dht_key2); - let dht_key2_string2 = String::from(&dht_key2); - let dht_key3_string = String::from(&dht_key3); - assert_eq!(dht_key2_string, dht_key2_string2); - assert_ne!(dht_key3_string, dht_key2_string); - let dht_key_secret2_string = String::from(&dht_key_secret2); - assert_ne!(dht_key_secret2_string, dht_key_secret_string); - assert_ne!(dht_key_secret2_string, dht_key2_string); + let public_key2_string = public_key2.to_string(); + let public_key2_string2 = public_key2.to_string(); + let public_key3_string = public_key3.to_string(); + assert_eq!(public_key2_string, public_key2_string2); + assert_ne!(public_key3_string, public_key2_string); + let secret_key2_string = secret_key2.to_string(); + assert_ne!(secret_key2_string, secret_key_string); + assert_ne!(secret_key2_string, public_key2_string); // Assert they convert back correctly - let dht_key_back = BarePublicKey::try_from(dht_key_string.as_str()).unwrap(); - let dht_key_back2 = BarePublicKey::try_from(dht_key_string2.as_str()).unwrap(); - assert_eq!(dht_key_back, dht_key_back2); - assert_eq!(dht_key_back, dht_key); - assert_eq!(dht_key_back2, dht_key); + let public_key_back = PublicKey::try_from(public_key_string.as_str()).unwrap(); + let public_key_back2 = PublicKey::try_from(public_key_string2.as_str()).unwrap(); + assert_eq!(public_key_back, public_key_back2); + assert_eq!(public_key_back, public_key); + assert_eq!(public_key_back2, public_key); - let dht_key_secret_back = BareSecretKey::try_from(dht_key_secret_string.as_str()).unwrap(); - assert_eq!(dht_key_secret_back, dht_key_secret); + let secret_key_back = SecretKey::try_from(secret_key_string.as_str()).unwrap(); + assert_eq!(secret_key_back, secret_key); - let dht_key2_back = BarePublicKey::try_from(dht_key2_string.as_str()).unwrap(); - let dht_key2_back2 = BarePublicKey::try_from(dht_key2_string2.as_str()).unwrap(); - assert_eq!(dht_key2_back, dht_key2_back2); - assert_eq!(dht_key2_back, dht_key2); - assert_eq!(dht_key2_back2, dht_key2); + let public_key2_back = PublicKey::try_from(public_key2_string.as_str()).unwrap(); + let public_key2_back2 = PublicKey::try_from(public_key2_string2.as_str()).unwrap(); + assert_eq!(public_key2_back, public_key2_back2); + assert_eq!(public_key2_back, public_key2); + assert_eq!(public_key2_back2, public_key2); - let dht_key_secret2_back = BareSecretKey::try_from(dht_key_secret2_string.as_str()).unwrap(); - assert_eq!(dht_key_secret2_back, dht_key_secret2); + let secret_key2_back = SecretKey::try_from(secret_key2_string.as_str()).unwrap(); + assert_eq!(secret_key2_back, secret_key2); // Assert string roundtrip - assert_eq!(String::from(&dht_key2_back), dht_key2_string); + assert_eq!(secret_key2_back.to_string(), secret_key2_string); + // These conversions should fail assert!(BarePublicKey::try_from("whatever!").is_err()); assert!(BareSecretKey::try_from("whatever!").is_err()); @@ -167,41 +193,42 @@ pub async fn test_key_conversions(vcrypto: &AsyncCryptoSystemGuard<'_>) { } pub async fn test_encode_decode(vcrypto: &AsyncCryptoSystemGuard<'_>) { - let dht_key = BarePublicKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(); - let dht_key_secret = + let public_key = + BarePublicKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(); + let secret_key = BareSecretKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(); - let dht_key_b = BarePublicKey::new(&EMPTY_KEY); - let dht_key_secret_b = BareSecretKey::new(&EMPTY_KEY_SECRET); - assert_eq!(dht_key, dht_key_b); - assert_eq!(dht_key_secret, dht_key_secret_b); + let public_key_b = BarePublicKey::new(&EMPTY_KEY); + let secret_key_b = BareSecretKey::new(&EMPTY_KEY_SECRET); + assert_eq!(public_key, public_key_b); + assert_eq!(secret_key, secret_key_b); - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split(); + let (public_key2, secret_key2) = vcrypto.generate_keypair().await.value().into_split(); - let e1 = dht_key.encode(); + let e1 = public_key.encode(); trace!("e1: {:?}", e1); assert_eq!(e1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned()); - let e1s = dht_key_secret.encode(); + let e1s = secret_key.encode(); trace!("e1s: {:?}", e1s); - let e2 = dht_key2.encode(); + let e2 = public_key2.encode(); trace!("e2: {:?}", e2); - let e2s = dht_key_secret2.encode(); + let e2s = secret_key2.encode(); trace!("e2s: {:?}", e2s); let d1 = BarePublicKey::try_decode(e1.as_str()).unwrap(); trace!("d1: {:?}", d1); - assert_eq!(dht_key, d1); + assert_eq!(public_key, d1); let d1s = BareSecretKey::try_decode(e1s.as_str()).unwrap(); trace!("d1s: {:?}", d1s); - assert_eq!(dht_key_secret, d1s); + assert_eq!(secret_key, d1s); let d2 = BarePublicKey::try_decode(e2.as_str()).unwrap(); trace!("d2: {:?}", d2); - assert_eq!(dht_key2, d2); + assert_eq!(public_key2, d2); let d2s = BareSecretKey::try_decode(e2s.as_str()).unwrap(); trace!("d2s: {:?}", d2s); - assert_eq!(dht_key_secret2, d2s); + assert_eq!(secret_key2, d2s); // Failures let f1 = BareSecretKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!"); @@ -216,7 +243,7 @@ pub fn test_typed_convert(vcrypto: &AsyncCryptoSystemGuard<'_>) { vcrypto.kind() ); let tk1 = PublicKey::from_str(&tks1).expect("failed"); - assert!(vcrypto.check_public_key(tk1.ref_value()).is_ok()); + assert!(vcrypto.check_public_key(&tk1).is_ok()); let tks1x = tk1.to_string(); assert_eq!(tks1, tks1x); @@ -231,7 +258,7 @@ pub fn test_typed_convert(vcrypto: &AsyncCryptoSystemGuard<'_>) { vcrypto.kind() ); let tk3 = PublicKey::from_str(&tks3).expect("failed"); - assert!(vcrypto.check_public_key(tk3.ref_value()).is_err()); + assert!(vcrypto.check_public_key(&tk3).is_err()); let tks4 = "XXXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ".to_string(); let tk4 = PublicKey::from_str(&tks4).expect("failed"); @@ -258,7 +285,7 @@ pub fn test_typed_convert(vcrypto: &AsyncCryptoSystemGuard<'_>) { } async fn test_hash(vcrypto: &AsyncCryptoSystemGuard<'_>) { - let mut s = BTreeSet::::new(); + let mut s = BTreeSet::::new(); let k1 = vcrypto.generate_hash("abc".as_bytes()).await; let k2 = vcrypto.generate_hash("abcd".as_bytes()).await; @@ -289,24 +316,43 @@ async fn test_hash(vcrypto: &AsyncCryptoSystemGuard<'_>) { assert_eq!(k5, v5); assert_eq!(k6, v6); - vcrypto.validate_hash("abc".as_bytes(), &v1).await; - vcrypto.validate_hash("abcd".as_bytes(), &v2).await; - vcrypto.validate_hash("".as_bytes(), &v3).await; - vcrypto.validate_hash(" ".as_bytes(), &v4).await; - vcrypto.validate_hash(LOREM_IPSUM, &v5).await; - vcrypto.validate_hash(CHEEZBURGER, &v6).await; + vcrypto + .validate_hash("abc".as_bytes(), &v1) + .await + .expect("should succeed"); + vcrypto + .validate_hash("abcd".as_bytes(), &v2) + .await + .expect("should succeed"); + vcrypto + .validate_hash("".as_bytes(), &v3) + .await + .expect("should succeed"); + vcrypto + .validate_hash(" ".as_bytes(), &v4) + .await + .expect("should succeed"); + vcrypto + .validate_hash(LOREM_IPSUM, &v5) + .await + .expect("should succeed"); + vcrypto + .validate_hash(CHEEZBURGER, &v6) + .await + .expect("should succeed"); } async fn test_operations(vcrypto: &AsyncCryptoSystemGuard<'_>) { + // xxx we should make this fixed byte arrays when we add another cryptosystem let k1 = vcrypto.generate_hash(LOREM_IPSUM).await; let k2 = vcrypto.generate_hash(CHEEZBURGER).await; let k3 = vcrypto.generate_hash("abc".as_bytes()).await; // Get distance - let d1 = vcrypto.distance(&k1, &k2).await; - let d2 = vcrypto.distance(&k2, &k1).await; - let d3 = vcrypto.distance(&k1, &k3).await; - let d4 = vcrypto.distance(&k2, &k3).await; + let d1 = k1.to_hash_coordinate().distance(&k2.to_hash_coordinate()); + let d2 = k2.to_hash_coordinate().distance(&k1.to_hash_coordinate()); + let d3 = k1.to_hash_coordinate().distance(&k3.to_hash_coordinate()); + let d4 = k2.to_hash_coordinate().distance(&k3.to_hash_coordinate()); trace!("d1={:?}", d1); trace!("d2={:?}", d2); diff --git a/veilid-core/src/crypto/types/byte_array_types.rs b/veilid-core/src/crypto/types/byte_array_types.rs index 7fab99b1..17bb8be8 100644 --- a/veilid-core/src/crypto/types/byte_array_types.rs +++ b/veilid-core/src/crypto/types/byte_array_types.rs @@ -10,52 +10,20 @@ use data_encoding::BASE64URL_NOPAD; ////////////////////////////////////////////////////////////////////// -pub trait Encodable -where - Self: Sized, -{ - fn encode(&self) -> String; - fn encoded_len(&self) -> usize; - fn try_decode>(input: S) -> VeilidAPIResult { - let b = input.as_ref().as_bytes(); - Self::try_decode_bytes(b) - } - fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult; -} - -////////////////////////////////////////////////////////////////////// -pub trait ByteArrayType -where - Self: Sized, -{ - fn len(&self) -> usize; - fn is_empty(&self) -> bool; - fn bytes(&self) -> &[u8]; - fn bit(&self, index: usize) -> bool; - fn first_nonzero_bit(&self) -> Option; - fn nibble(&self, index: usize) -> u8; - fn first_nonzero_nibble(&self) -> Option<(usize, u8)>; -} - -////////////////////////////////////////////////////////////////////// - macro_rules! byte_array_type { - ($name:ident) => { - #[derive(Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] - #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] - #[cfg_attr( - all(target_arch = "wasm32", target_os = "unknown"), - tsify(from_wasm_abi, into_wasm_abi) - )] - #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), serde(transparent))] + ($visibility:vis $name:ident) => { + + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(wasm_bindgen_derive::TryFromJsValue))] + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] + #[derive(Clone, Hash, Default, PartialOrd, Ord, PartialEq, Eq)] #[must_use] - pub struct $name { - #[cfg_attr( - all(target_arch = "wasm32", target_os = "unknown"), - tsify(type = "string") - )] + $visibility struct $name { bytes: Bytes, } + + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + make_wasm_bindgen_stubs!($name); + impl $name { pub fn new(data: &[u8]) -> Self { Self { @@ -65,32 +33,69 @@ macro_rules! byte_array_type { fn new_from_bytes(bytes: Bytes) -> Self { Self { bytes } } - } - impl Default for $name { - fn default() -> Self { - Self { - bytes: Bytes::new(), - } - } - } - impl ByteArrayType for $name { - fn len(&self) -> usize { - self.bytes.len() - } - fn is_empty(&self) -> bool { - self.bytes.is_empty() - } - fn bytes(&self) -> &[u8] { + + pub fn bytes(&self) -> &[u8] { &self.bytes } + + #[allow(dead_code)] + pub fn first_nonzero_nibble(&self) -> Option<(usize, u8)> { + for i in 0..(self.bytes.len() * 2) { + let n = self.nibble(i); + if n != 0 { + return Some((i, n)); + } + } + None + } + } + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + #[wasm_bindgen] + impl $name { + #[wasm_bindgen(constructor)] + pub fn js_new(data: &[u8]) -> Self { + Self::new(data) + } + + #[wasm_bindgen(js_name = parse)] + pub fn js_parse(s: String) -> VeilidAPIResult { + Self::from_str(&s) + } + + #[wasm_bindgen(js_name = toString)] + pub fn js_to_string(&self) -> String { + self.to_string() + } + + #[wasm_bindgen(js_name = isEqual)] + pub fn js_is_equal(&self, other: &Self) -> bool { + self == other + } + + // TODO: add more typescript-only operations here + } + + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] + #[allow(dead_code)] + impl $name { + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter, js_name = length))] + pub fn len(&self) -> usize { + self.bytes.len() + } + pub fn is_empty(&self) -> bool { + self.bytes.is_empty() + } + pub fn to_vec(&self) -> Vec { + self.bytes.to_vec() + } // Big endian bit ordering - fn bit(&self, index: usize) -> bool { + pub fn bit(&self, index: usize) -> bool { let bi = index / 8; let ti = 7 - (index % 8); ((self.bytes[bi] >> ti) & 1) != 0 } - fn first_nonzero_bit(&self) -> Option { + pub fn first_nonzero_bit(&self) -> Option { for i in 0..self.bytes.len() { let b = self.bytes[i]; if b != 0 { @@ -106,7 +111,7 @@ macro_rules! byte_array_type { } // Big endian nibble ordering - fn nibble(&self, index: usize) -> u8 { + pub fn nibble(&self, index: usize) -> u8 { let bi = index / 2; if index & 1 == 0 { (self.bytes[bi] >> 4) & 0xFu8 @@ -115,24 +120,17 @@ macro_rules! byte_array_type { } } - fn first_nonzero_nibble(&self) -> Option<(usize, u8)> { - for i in 0..(self.bytes.len() * 2) { - let n = self.nibble(i); - if n != 0 { - return Some((i, n)); - } - } - None - } - } - impl Encodable for $name { - fn encode(&self) -> String { + pub fn encode(&self) -> String { BASE64URL_NOPAD.encode(&self.bytes) } - fn encoded_len(&self) -> usize { + pub fn encoded_len(&self) -> usize { BASE64URL_NOPAD.encode_len(self.bytes.len()) } - fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult { + pub fn try_decode(input: &str) -> VeilidAPIResult { + let b = input.as_bytes(); + Self::try_decode_bytes(b) + } + pub fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult { if b.len() == 0 { return Ok(Self::default()); } @@ -218,9 +216,10 @@ macro_rules! byte_array_type { } } - impl From<$name> for Vec { - fn from(value: $name) -> Self { - value.bytes().to_vec() + impl TryFrom<$name> for Vec { + type Error = VeilidAPIError; + fn try_from(value: $name) -> Result { + Ok(value.bytes().to_vec()) } } @@ -243,75 +242,36 @@ macro_rules! byte_array_type { } } } - }; + } } ///////////////////////////////////////// -byte_array_type!(BarePublicKey); -byte_array_type!(BareSecretKey); -byte_array_type!(BareEncapsulationKey); -byte_array_type!(BareDecapsulationKey); -byte_array_type!(BareSignature); -byte_array_type!(BareNonce); +// Untyped public key (variable length) +byte_array_type!(pub BarePublicKey); +// Untyped secret key (variable length) +byte_array_type!(pub BareSecretKey); +// Untyped encapsulation key (variable length) +byte_array_type!(pub BareEncapsulationKey); +// Untyped decapsulation key (variable length) +byte_array_type!(pub BareDecapsulationKey); +// Untyped signature (variable length) +byte_array_type!(pub BareSignature); +// Untyped hash digest (hashed to 32 bytes) +byte_array_type!(pub BareHashDigest); +// Untyped shared secret (variable length) +byte_array_type!(pub BareSharedSecret); +// Untyped record key (hashed to 32 bytes) +byte_array_type!(pub BareRecordKey); +// Untyped route id (hashed to 32 bytes) +byte_array_type!(pub BareRouteId); +// Untyped node id (hashed to 32 bytes) +byte_array_type!(pub BareNodeId); +// Untyped member id (hashed to 32 bytes) +byte_array_type!(pub BareMemberId); +// Untyped nonce (random 24 bytes, no typed variant) +byte_array_type!(pub Nonce); -/* -Notes: - - These are actually BareHashDigest types, but not interchangable: - - BareSharedSecret - - BareRecordKey - - BareRouteId (eventually will be a BareRecordKey type with DHT Routes) - - BareNodeId (constructible from BarePublicKey) - - BareMemberId (constructible from BarePublicKey) -*/ - -// BareHashDigest sub-types -byte_array_type!(BareHashDigest); -byte_array_type!(BareSharedSecret); -byte_array_type!(BareRecordKey); -byte_array_type!(BareHashDistance); -byte_array_type!(BareRouteId); -byte_array_type!(BareNodeId); -byte_array_type!(BareMemberId); - -// Temporary adapters for converting to/from BareHashDigest types -// Removing these will show where there's still issues. -impl From for BareSharedSecret { - fn from(value: BareHashDigest) -> Self { - Self::new(value.bytes()) - } -} - -impl From for BareRecordKey { - fn from(value: BareHashDigest) -> Self { - Self::new(value.bytes()) - } -} - -impl From for BareHashDigest { - fn from(value: BareRecordKey) -> Self { - Self::new(value.bytes()) - } -} - -impl From for BareHashDigest { - fn from(value: BareNodeId) -> Self { - Self::new(value.bytes()) - } -} - -/* -- BareNodeId currently equals BarePublicKey, but should be distinct from BarePublicKey. - - BareNodeId eventually should be a BareHashDigest type that's constructable from a BarePublicKey -*/ -impl From for BareNodeId { - fn from(value: BarePublicKey) -> Self { - Self::new(value.bytes()) - } -} - -impl From for BarePublicKey { - fn from(value: BareNodeId) -> Self { - Self::new(value.bytes()) - } -} +// Internal types +byte_array_type!(pub(crate) BareHashCoordinate); +byte_array_type!(pub(crate) HashDistance); diff --git a/veilid-core/src/crypto/types/crypto_typed.rs b/veilid-core/src/crypto/types/crypto_typed.rs index a87b204c..8191bae4 100644 --- a/veilid-core/src/crypto/types/crypto_typed.rs +++ b/veilid-core/src/crypto/types/crypto_typed.rs @@ -1,209 +1,191 @@ -use super::*; +#[macro_export] +macro_rules! impl_crypto_typed { + ($visibility:vis $name:ident) => { + paste::paste! { -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[must_use] -pub struct CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, -{ - kind: CryptoKind, - value: K, -} + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(wasm_bindgen_derive::TryFromJsValue))] + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + #[must_use] + $visibility struct $name + { + kind: CryptoKind, + value: [], + } -cfg_if::cfg_if! { - if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { - #[wasm_bindgen(typescript_custom_section)] - const CRYPOTYPED_TYPE: &'static str = r#" -export type CryptoTyped = `${CryptoKind}:${TCryptoKey}`; -"#; - } -} + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + make_wasm_bindgen_stubs!($name); -impl CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, -{ - pub fn new(kind: CryptoKind, value: K) -> Self { - Self { kind, value } - } + impl $name { + pub fn new(kind: CryptoKind, value: []) -> Self { + Self { kind, value } + } - pub fn kind(&self) -> CryptoKind { - self.kind - } - pub fn value(&self) -> K { - self.value.clone() - } - pub fn ref_value(&self) -> &K { - &self.value - } - pub fn into_value(self) -> K { - self.value - } -} + pub fn ref_value(&self) -> &[] { + &self.value + } + #[allow(dead_code)] + pub fn into_value(self) -> [] { + self.value + } + } -impl PartialOrd for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: Ord + PartialOrd, -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] + impl $name { + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter, unchecked_return_type = "CryptoKind"))] + pub fn kind(&self) -> CryptoKind { + self.kind + } + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter))] + #[allow(dead_code)] + pub fn value(&self) -> [] { + self.value.clone() + } + } -impl Ord for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: Ord + PartialOrd, -{ - fn cmp(&self, other: &Self) -> cmp::Ordering { - let x = compare_crypto_kind(&self.kind, &other.kind); - if x != cmp::Ordering::Equal { - return x; + impl PartialOrd for $name + { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for $name + { + fn cmp(&self, other: &Self) -> cmp::Ordering { + let x = compare_crypto_kind(&self.kind, &other.kind); + if x != cmp::Ordering::Equal { + return x; + } + self.value.cmp(&other.value) + } + } + + impl fmt::Display for $name + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}:{}", self.kind, self.value) + } + } + + impl FromStr for $name + { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let b = s.as_bytes(); + if b.len() > 5 && b[4..5] == b":"[..] { + let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert"); + let value = []::try_decode_bytes(&b[5..])?; + Ok(Self { kind, value }) + } else { + let kind = best_crypto_kind(); + let value = []::try_decode_bytes(b)?; + Ok(Self { kind, value }) + } + } + } + + impl TryFrom for $name + { + type Error = VeilidAPIError; + + fn try_from(s: String) -> Result { + Self::from_str(&s) + } + } + + impl TryFrom<&str> for $name + { + type Error = VeilidAPIError; + + fn try_from(s: &str) -> Result { + Self::from_str(s) + } + } + + impl<'de> Deserialize<'de> for $name + { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(serde::de::Error::custom) + } + } + impl Serialize for $name + { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.collect_str(self) + } + } + + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + #[wasm_bindgen] + impl $name { + #[wasm_bindgen(constructor)] + pub fn js_new(kind: CryptoKind, value: []) -> Self { + Self::new(kind,value) + } + + #[wasm_bindgen(js_name = parse)] + pub fn js_parse(s: String) -> VeilidAPIResult { + Self::from_str(&s) + } + + #[wasm_bindgen(js_name = toString)] + pub fn js_to_string(&self) -> String { + self.to_string() + } + + #[wasm_bindgen(js_name = isEqual)] + pub fn js_is_equal(&self, other: &Self) -> bool { + self == other + } + // TODO: add more typescript-only operations here + } } - self.value.cmp(&other.value) - } + }; } -impl fmt::Display for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}:{}", self.kind, self.value) - } -} +#[macro_export] +macro_rules! impl_crypto_typed_vec { + ($visibility:vis $name:ident) => { + paste::paste! { + impl<'a> TryFrom<&'a [u8]> for $name + { + type Error = VeilidAPIError; -impl FromStr for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: Encodable, -{ - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let b = s.as_bytes(); - if b.len() > 5 && b[4..5] == b":"[..] { - let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert"); - let value = K::try_decode_bytes(&b[5..])?; - Ok(Self { kind, value }) - } else { - let kind = best_crypto_kind(); - let value = K::try_decode_bytes(b)?; - Ok(Self { kind, value }) + fn try_from(b: &'a [u8]) -> Result { + if b.len() < 4 { + apibail_generic!("invalid cryptotyped format"); + } + let kind: CryptoKind = b[0..4].try_into()?; + let value: [] = b[4..].into(); + Ok(Self { kind, value }) + } + } + + impl TryFrom> for $name + { + type Error = VeilidAPIError; + + fn try_from(b: Vec) -> Result { + Self::try_from(b.as_slice()) + } + } + + impl From<$name> for Vec + { + fn from(v: $name) -> Self { + let mut out = v.kind.0.to_vec(); + out.extend_from_slice(v.value.as_ref()); + out + } + } } - } -} - -impl TryFrom for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: Encodable, -{ - type Error = VeilidAPIError; - - fn try_from(s: String) -> Result { - Self::from_str(&s) - } -} - -impl TryFrom<&str> for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: Encodable, -{ - type Error = VeilidAPIError; - - fn try_from(s: &str) -> Result { - Self::from_str(s) - } -} - -impl<'a, K> TryFrom<&'a [u8]> for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: From<&'a [u8]>, -{ - type Error = VeilidAPIError; - - fn try_from(b: &'a [u8]) -> Result { - if b.len() < 4 { - apibail_generic!("invalid cryptotyped format"); - } - let kind: CryptoKind = b[0..4].try_into()?; - let value: K = b[4..].into(); - Ok(Self { kind, value }) - } -} - -impl TryFrom> for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: for<'a> From<&'a [u8]>, -{ - type Error = VeilidAPIError; - - fn try_from(b: Vec) -> Result { - Self::try_from(b.as_slice()) - } -} - -impl From> for Vec -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: AsRef<[u8]>, -{ - fn from(v: CryptoTyped) -> Self { - let mut out = v.kind.0.to_vec(); - out.extend_from_slice(v.value.as_ref()); - out - } -} - -impl<'de, K> Deserialize<'de> for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: Encodable, -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - FromStr::from_str(&s).map_err(serde::de::Error::custom) - } -} -impl Serialize for CryptoTyped -where - K: Clone + fmt::Debug + PartialEq + Eq + Hash, - K: fmt::Display, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.collect_str(self) - } -} - -impl CryptoTyped { - pub fn new_from_parts(key: PublicKey, bare_secret: BareSecretKey) -> Self { - Self { - kind: key.kind(), - value: BareKeyPair::new(key.value(), bare_secret), - } - } - - pub fn key(&self) -> PublicKey { - PublicKey::new(self.kind, self.ref_value().key()) - } - pub fn secret(&self) -> SecretKey { - SecretKey::new(self.kind, self.ref_value().secret()) - } - pub fn bare_secret(&self) -> BareSecretKey { - self.ref_value().secret() - } - pub fn ref_bare_secret(&self) -> &BareSecretKey { - self.ref_value().ref_secret() - } + }; } diff --git a/veilid-core/src/crypto/types/crypto_typed_group.rs b/veilid-core/src/crypto/types/crypto_typed_group.rs index 9a3bfc71..9d8eda82 100644 --- a/veilid-core/src/crypto/types/crypto_typed_group.rs +++ b/veilid-core/src/crypto/types/crypto_typed_group.rs @@ -1,301 +1,282 @@ -use super::*; +#[macro_export] +macro_rules! impl_crypto_typed_group { + ($visibility:vis $name:ident) => { + paste::paste! { -#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash, Default)] -#[serde(from = "Vec>", into = "Vec>")] -pub struct CryptoTypedGroup -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - items: Vec>, -} - -cfg_if::cfg_if! { - if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { - #[wasm_bindgen(typescript_custom_section)] - const CRYPOTYPEDGROUP_TYPE: &'static str = r#" -export type CryptoTypedGroup = Array>; -"#; - } -} - -impl CryptoTypedGroup -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - #[must_use] - pub fn new() -> Self { - Self { items: Vec::new() } - } - #[must_use] - pub fn with_capacity(cap: usize) -> Self { - Self { - items: Vec::with_capacity(cap), - } - } - pub fn kinds(&self) -> Vec { - let mut out = Vec::new(); - for tk in &self.items { - out.push(tk.kind()); - } - out.sort_by(compare_crypto_kind); - out - } - #[must_use] - pub fn keys(&self) -> Vec { - let mut out = Vec::new(); - for tk in &self.items { - out.push(tk.value()); - } - out - } - #[must_use] - pub fn get(&self, kind: CryptoKind) -> Option> { - self.items.iter().find(|x| x.kind() == kind).cloned() - } - pub fn add(&mut self, typed_key: CryptoTyped) { - for x in &mut self.items { - if x.kind() == typed_key.kind() { - *x = typed_key; - return; + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(wasm_bindgen_derive::TryFromJsValue))] + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] + #[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash, Default)] + #[serde(from = "Vec<_>", into = "Vec<_>")] + pub struct [<$name Group>] + { + items: Vec<$name>, } - } - self.items.push(typed_key); - self.items.sort() - } - pub fn add_all(&mut self, typed_keys: &[CryptoTyped]) { - 'outer: for typed_key in typed_keys { - for x in &mut self.items { - if x.kind() == typed_key.kind() { - *x = typed_key.clone(); - continue 'outer; + + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + make_wasm_bindgen_stubs!([<$name Group>]); + + impl [<$name Group>] + { + #[must_use] + pub fn new() -> Self { + Self { items: Vec::new() } + } + #[must_use] + pub fn with_capacity(cap: usize) -> Self { + Self { + items: Vec::with_capacity(cap), + } + } + + pub fn iter(&self) -> core::slice::Iter<'_, $name> { + self.items.iter() + } + + pub fn add_all_from_slice(&mut self, typed_keys: &[$name]) { + 'outer: for typed_key in typed_keys { + for x in &mut self.items { + if x.kind() == typed_key.kind() { + *x = typed_key.clone(); + continue 'outer; + } + } + self.items.push(typed_key.clone()); + } + self.items.sort() + } + + pub fn contains_any_from_slice(&self, typed_keys: &[$name]) -> bool { + for typed_key in typed_keys { + if self.items.contains(typed_key) { + return true; + } + } + false + } + + } + + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + #[wasm_bindgen] + impl [<$name Group>] + { + #[must_use] + pub fn get(&self, + #[wasm_bindgen(unchecked_param_type = "CryptoKind")] + kind: CryptoKind) -> Option<$name> { + self.items.iter().find(|x| x.kind() == kind).cloned() + } + + pub fn remove(&mut self, + #[wasm_bindgen(unchecked_param_type = "CryptoKind")] + kind: CryptoKind) -> Option<$name> { + if let Some(idx) = self.items.iter().position(|x| x.kind() == kind) { + return Some(self.items.remove(idx)); + } + None + } + + pub fn remove_all(&mut self, + #[wasm_bindgen(unchecked_param_type = "CryptoKind[]")] + kinds: Vec) { + for k in kinds { + self.remove(k); + } } } - self.items.push(typed_key.clone()); - } - self.items.sort() - } - pub fn remove(&mut self, kind: CryptoKind) -> Option> { - if let Some(idx) = self.items.iter().position(|x| x.kind() == kind) { - return Some(self.items.remove(idx)); - } - None - } - pub fn remove_all(&mut self, kinds: &[CryptoKind]) { - for k in kinds { - self.remove(*k); - } - } - pub fn clear(&mut self) { - self.items.clear(); - } + #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] + impl [<$name Group>] + { + #[must_use] + pub fn get(&self, kind: CryptoKind) -> Option<$name> { + self.items.iter().find(|x| x.kind() == kind).cloned() + } - /// Return preferred typed key of our supported crypto kinds - #[must_use] - pub fn best(&self) -> Option> { - self.items - .iter() - .find(|k| VALID_CRYPTO_KINDS.contains(&k.kind())) - .cloned() - } - #[must_use] - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } - #[must_use] - pub fn len(&self) -> usize { - self.items.len() - } - pub fn iter(&self) -> core::slice::Iter<'_, CryptoTyped> { - self.items.iter() - } - pub fn contains(&self, typed_key: &CryptoTyped) -> bool { - self.items.contains(typed_key) - } - pub fn contains_any(&self, typed_keys: &[CryptoTyped]) -> bool { - for typed_key in typed_keys { - if self.items.contains(typed_key) { - return true; + pub fn remove(&mut self, kind: CryptoKind) -> Option<$name> { + if let Some(idx) = self.items.iter().position(|x| x.kind() == kind) { + return Some(self.items.remove(idx)); + } + None + } + + pub fn remove_all(&mut self, kinds: Vec) { + for k in kinds { + self.remove(k); + } + } + } + + + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] + impl [<$name Group>] { + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter, unchecked_return_type = "CryptoKind[]"))] + pub fn kinds(&self) -> Vec { + let mut out = Vec::new(); + for tk in &self.items { + out.push(tk.kind()); + } + out.sort_by(compare_crypto_kind); + out + } + + #[must_use] + pub fn keys(&self) -> Vec<[]> { + let mut out = Vec::<[]>::new(); + for tk in &self.items { + out.push(tk.value()); + } + out + } + #[must_use] + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } + #[must_use] + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter, js_name = length))] + pub fn len(&self) -> usize { + self.items.len() + } + pub fn contains(&self, typed_key: &$name) -> bool { + self.items.contains(typed_key) + } + + #[must_use] + pub fn contains_any(&self, typed_keys: Vec<$name>) -> bool { + self.contains_any_from_slice(&typed_keys) + } + + #[must_use] + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(js_name = toArray))] + pub fn to_vec(&self) -> Vec<$name> { + self.items.clone() + } + + pub fn add(&mut self, typed_key: $name) { + for x in &mut self.items { + if x.kind() == typed_key.kind() { + *x = typed_key; + return; + } + } + self.items.push(typed_key); + self.items.sort() + } + + pub fn add_all(&mut self, typed_keys: Vec<$name>) { + self.add_all_from_slice(&typed_keys) + } + + pub fn clear(&mut self) { + self.items.clear(); + } + + + } + + impl core::ops::Deref for [<$name Group>] + { + type Target = [$name]; + + #[inline] + fn deref(&self) -> &[$name] { + &self.items + } + } + + impl fmt::Display for [<$name Group>] + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "[")?; + let mut first = true; + for x in &self.items { + if first { + first = false; + } else { + write!(f, ",")?; + } + write!(f, "{}", x)?; + } + write!(f, "]") + } + } + impl FromStr for [<$name Group>] + { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let mut items = Vec::new(); + if s.len() < 2 { + apibail_parse_error!("invalid length", s); + } + if &s[0..1] != "[" || &s[(s.len() - 1)..] != "]" { + apibail_parse_error!("invalid format", s); + } + for x in s[1..s.len() - 1].split(',') { + let tk = $name::from_str(x.trim())?; + items.push(tk); + } + + Ok(Self { items }) + } + } + impl From<$name> for [<$name Group>] + { + fn from(x: $name) -> Self { + let mut tks = [<$name Group>]::with_capacity(1); + tks.add(x); + tks + } + } + impl From> for [<$name Group>] + { + fn from(x: Vec<$name>) -> Self { + let mut tks = [<$name Group>]::with_capacity(x.len()); + tks.add_all_from_slice(&x); + tks + } + } + impl From<&[$name]> for [<$name Group>] + { + fn from(x: &[$name]) -> Self { + let mut tks = [<$name Group>]::with_capacity(x.len()); + tks.add_all_from_slice(x); + tks + } + } + impl From<[<$name Group>]> for Vec<$name> + { + fn from(val: [<$name Group>]) -> Self { + val.items + } + } + + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + #[wasm_bindgen] + impl [<$name Group>] { + #[wasm_bindgen(constructor)] + #[must_use] + pub fn js_new() -> Self { + Self::new() + } + + #[wasm_bindgen(js_name = parse)] + pub fn js_parse(s: String) -> VeilidAPIResult { + Self::from_str(&s) + } + + #[wasm_bindgen(js_name = toString)] + #[must_use] + pub fn js_to_string(&self) -> String { + self.to_string() + } + + #[wasm_bindgen(js_name = isEqual)] + #[must_use] + pub fn js_is_equal(&self, other: &Self) -> bool { + self == other + } + + // TODO: add more typescript-only operations here } } - false - } - pub fn contains_value(&self, value: &K) -> bool { - for tk in &self.items { - if tk.ref_value() == value { - return true; - } - } - false - } -} - -impl core::ops::Deref for CryptoTypedGroup -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - type Target = [CryptoTyped]; - - #[inline] - fn deref(&self) -> &[CryptoTyped] { - &self.items - } -} - -impl fmt::Display for CryptoTypedGroup -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "[")?; - let mut first = true; - for x in &self.items { - if first { - first = false; - } else { - write!(f, ",")?; - } - write!(f, "{}", x)?; - } - write!(f, "]") - } -} -impl FromStr for CryptoTypedGroup -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - let mut items = Vec::new(); - if s.len() < 2 { - apibail_parse_error!("invalid length", s); - } - if &s[0..1] != "[" || &s[(s.len() - 1)..] != "]" { - apibail_parse_error!("invalid format", s); - } - for x in s[1..s.len() - 1].split(',') { - let tk = CryptoTyped::::from_str(x.trim())?; - items.push(tk); - } - - Ok(Self { items }) - } -} -impl From> for CryptoTypedGroup -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - fn from(x: CryptoTyped) -> Self { - let mut tks = CryptoTypedGroup::::with_capacity(1); - tks.add(x); - tks - } -} -impl From>> for CryptoTypedGroup -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - fn from(x: Vec>) -> Self { - let mut tks = CryptoTypedGroup::::with_capacity(x.len()); - tks.add_all(&x); - tks - } -} -impl From<&[CryptoTyped]> for CryptoTypedGroup -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - fn from(x: &[CryptoTyped]) -> Self { - let mut tks = CryptoTypedGroup::::with_capacity(x.len()); - tks.add_all(x); - tks - } -} -impl From> for Vec> -where - K: Clone - + fmt::Debug - + fmt::Display - + FromStr - + PartialEq - + Eq - + PartialOrd - + Ord - + Hash - + Encodable, -{ - fn from(val: CryptoTypedGroup) -> Self { - val.items - } + }; } diff --git a/veilid-core/src/crypto/types/keypair.rs b/veilid-core/src/crypto/types/keypair.rs index bff071e9..2053b2c0 100644 --- a/veilid-core/src/crypto/types/keypair.rs +++ b/veilid-core/src/crypto/types/keypair.rs @@ -1,56 +1,66 @@ use super::*; -#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(wasm_bindgen_derive::TryFromJsValue) +)] +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] +#[derive(Clone, Default, Hash, PartialOrd, Ord, PartialEq, Eq)] #[must_use] pub struct BareKeyPair { key: BarePublicKey, secret: BareSecretKey, } -cfg_if::cfg_if! { - if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { - #[wasm_bindgen(typescript_custom_section)] - const KEYPAIR_TYPE: &'static str = r#" -export type BareKeyPair = `${BarePublicKey}:${BareSecretKey}`; -"#; - } -} - impl BareKeyPair { pub fn new(key: BarePublicKey, secret: BareSecretKey) -> Self { Self { key, secret } } - pub fn key(&self) -> BarePublicKey { - self.key.clone() - } - pub fn secret(&self) -> BareSecretKey { - self.secret.clone() - } pub fn ref_key(&self) -> &BarePublicKey { &self.key } pub fn ref_secret(&self) -> &BareSecretKey { &self.secret } - pub fn split(&self) -> (BarePublicKey, BareSecretKey) { - (self.key.clone(), self.secret.clone()) - } pub fn ref_split(&self) -> (&BarePublicKey, &BareSecretKey) { (&self.key, &self.secret) } + pub fn split(&self) -> (BarePublicKey, BareSecretKey) { + (self.key.clone(), self.secret.clone()) + } pub fn into_split(self) -> (BarePublicKey, BareSecretKey) { (self.key, self.secret) } } -impl Encodable for BareKeyPair { - fn encode(&self) -> String { +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] +#[allow(dead_code)] +impl BareKeyPair { + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn key(&self) -> BarePublicKey { + self.key.clone() + } + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn secret(&self) -> BareSecretKey { + self.secret.clone() + } + pub fn encode(&self) -> String { format!("{}:{}", self.key.encode(), self.secret.encode()) } - fn encoded_len(&self) -> usize { + pub fn encoded_len(&self) -> usize { self.key.encoded_len() + 1 + self.secret.encoded_len() } - fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult { + pub fn try_decode(input: &str) -> VeilidAPIResult { + let b = input.as_bytes(); + Self::try_decode_bytes(b) + } + pub fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult { let parts: Vec<_> = b.split(|x| *x == b':').collect(); if parts.len() != 2 { apibail_parse_error!( @@ -63,6 +73,7 @@ impl Encodable for BareKeyPair { Ok(BareKeyPair { key, secret }) } } + impl fmt::Display for BareKeyPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.encode()) @@ -125,3 +136,50 @@ impl<'de> serde::Deserialize<'de> for BareKeyPair { BareKeyPair::try_decode(s.as_str()).map_err(serde::de::Error::custom) } } + +//////////////////////////////////////////////////////////////////////////// + +impl KeyPair { + pub fn into_split(self) -> (PublicKey, SecretKey) { + let kind = self.kind; + let (pk, sk) = self.into_value().into_split(); + (PublicKey::new(kind, pk), SecretKey::new(kind, sk)) + } + + pub fn ref_bare_secret(&self) -> &BareSecretKey { + self.ref_value().ref_secret() + } +} + +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] +#[allow(dead_code)] +impl KeyPair { + pub fn new_from_parts(key: PublicKey, bare_secret: BareSecretKey) -> Self { + Self { + kind: key.kind(), + value: BareKeyPair::new(key.value(), bare_secret), + } + } + + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn key(&self) -> PublicKey { + PublicKey::new(self.kind, self.ref_value().key()) + } + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn secret(&self) -> SecretKey { + SecretKey::new(self.kind, self.ref_value().secret()) + } + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn bare_secret(&self) -> BareSecretKey { + self.ref_value().secret() + } +} diff --git a/veilid-core/src/crypto/types/mod.rs b/veilid-core/src/crypto/types/mod.rs index cf1b97a8..da876bf8 100644 --- a/veilid-core/src/crypto/types/mod.rs +++ b/veilid-core/src/crypto/types/mod.rs @@ -48,94 +48,38 @@ mod crypto_typed_group; mod keypair; pub use byte_array_types::*; -pub use crypto_typed::*; -pub use crypto_typed_group::*; pub use keypair::*; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type EncapsulationKey = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type DecapsulationKey = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type PublicKey = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type SecretKey = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type KeyPair = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type Signature = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type SharedSecret = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type HashDigest = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type RecordKey = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type NodeId = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type RouteId = CryptoTyped; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type MemberId = CryptoTyped; - -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type EncapsulationKeyGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type DecapsulationKeyGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type PublicKeyGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type SecretKeyGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type KeyPairGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type SignatureGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type SharedSecretGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type HashDigestGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type RecordKeyGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type NodeIdGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type RouteIdGroup = CryptoTypedGroup; -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)] -pub type MemberIdGroup = CryptoTypedGroup; - -impl From for HashDigest { - fn from(value: NodeId) -> Self { - HashDigest::new(value.kind(), value.into_value().into()) - } +macro_rules! impl_crypto_typed_and_group { + ($visibility:vis $name:ident) => { + impl_crypto_typed!($visibility $name); + impl_crypto_typed_group!($visibility $name); + }; } -impl From for HashDigest { - fn from(value: RecordKey) -> Self { - HashDigest::new(value.kind(), value.into_value().into()) - } +macro_rules! impl_crypto_typed_and_group_and_vec { + ($visibility:vis $name:ident) => { + impl_crypto_typed!($visibility $name); + impl_crypto_typed_group!($visibility $name); + impl_crypto_typed_vec!($visibility $name); + }; } -impl From for PublicKey { - fn from(value: NodeId) -> Self { - PublicKey::new(value.kind(), value.into_value().into()) - } -} +// CryptoKind typed, with group and vector conversions +impl_crypto_typed_and_group_and_vec!(pub EncapsulationKey); +impl_crypto_typed_and_group_and_vec!(pub DecapsulationKey); +impl_crypto_typed_and_group_and_vec!(pub PublicKey); +impl_crypto_typed_and_group_and_vec!(pub SecretKey); +impl_crypto_typed_and_group_and_vec!(pub Signature); +impl_crypto_typed_and_group_and_vec!(pub SharedSecret); +impl_crypto_typed_and_group_and_vec!(pub HashDigest); +impl_crypto_typed_and_group_and_vec!(pub RecordKey); +impl_crypto_typed_and_group_and_vec!(pub NodeId); +impl_crypto_typed_and_group_and_vec!(pub RouteId); +impl_crypto_typed_and_group_and_vec!(pub MemberId); -impl From for NodeId { - fn from(value: PublicKey) -> Self { - NodeId::new(value.kind(), value.into_value().into()) - } -} +// No vector representation +impl_crypto_typed_and_group!(pub KeyPair); -impl From for PublicKeyGroup { - fn from(value: NodeIdGroup) -> Self { - let items: Vec = value.iter().map(|node_id| node_id.clone().into()).collect(); - PublicKeyGroup::from(items) - } -} - -impl From for NodeIdGroup { - fn from(value: PublicKeyGroup) -> Self { - let items: Vec = value.iter().map(|node_id| node_id.clone().into()).collect(); - NodeIdGroup::from(items) - } -} +// Internal types +impl_crypto_typed!(pub(crate) HashCoordinate); diff --git a/veilid-core/src/network_manager/bootstrap/bootstrap_record.rs b/veilid-core/src/network_manager/bootstrap/bootstrap_record.rs index 261be3bf..299e00f9 100644 --- a/veilid-core/src/network_manager/bootstrap/bootstrap_record.rs +++ b/veilid-core/src/network_manager/bootstrap/bootstrap_record.rs @@ -49,7 +49,7 @@ impl BootstrapRecord { } pub fn merge(&mut self, other: BootstrapRecord) { - self.public_keys.add_all(&other.public_keys); + self.public_keys.add_all_from_slice(&other.public_keys); for x in other.envelope_support { if !self.envelope_support.contains(&x) { self.envelope_support.push(x); @@ -83,7 +83,7 @@ impl BootstrapRecord { .envelope_support() .iter() .map(|x| { - if (x.0)[0..3] == *b"ENV" { + if x.bytes()[0..3] == *b"ENV" { x.to_string().split_off(3) } else { x.to_string() @@ -161,19 +161,20 @@ impl BootstrapRecord { let crypto = network_manager.crypto(); - let sig = match crypto.generate_signatures(v1.as_bytes(), &[signing_key_pair], |kp, sig| { - Signature::new(kp.kind(), sig).to_string() - }) { - Ok(v) => { - let Some(sig) = v.first().cloned() else { - bail!("No signature generated"); - }; - sig - } - Err(e) => { - bail!("Failed to generate signature: {}", e); - } - }; + let sig = + match crypto.generate_signatures(v1.as_bytes(), &[signing_key_pair], |_kp, sig| { + sig.to_string() + }) { + Ok(v) => { + let Some(sig) = v.first().cloned() else { + bail!("No signature generated"); + }; + sig + } + Err(e) => { + bail!("Failed to generate signature: {}", e); + } + }; v1 += &sig; Ok(v1) diff --git a/veilid-core/src/network_manager/bootstrap/direct_bootstrap/mod.rs b/veilid-core/src/network_manager/bootstrap/direct_bootstrap/mod.rs index f8719c4c..996a2b45 100644 --- a/veilid-core/src/network_manager/bootstrap/direct_bootstrap/mod.rs +++ b/veilid-core/src/network_manager/bootstrap/direct_bootstrap/mod.rs @@ -112,7 +112,7 @@ impl NetworkManager { // and as such, a routing domain can not be determined for it // by the code that receives the FindNodeA result for pi in bootv1response.peers.iter().cloned() { - if pi.node_info().public_keys().contains_any(bsrec.public_keys()) { + if pi.node_info().public_keys().contains_any_from_slice(bsrec.public_keys()) { return Some(pi); } } diff --git a/veilid-core/src/network_manager/bootstrap/direct_bootstrap/v1.rs b/veilid-core/src/network_manager/bootstrap/direct_bootstrap/v1.rs index 555df249..9434f021 100644 --- a/veilid-core/src/network_manager/bootstrap/direct_bootstrap/v1.rs +++ b/veilid-core/src/network_manager/bootstrap/direct_bootstrap/v1.rs @@ -75,8 +75,8 @@ impl NetworkManager { .filter_map(|bsrec| { if routing_table.matches_own_public_key(bsrec.public_keys()) { routing_table.get_published_peer_info(routing_domain) - } else if let Some(best_public_key) = bsrec.public_keys().best() { - if let Ok(best_node_id) = routing_table.generate_node_id(&best_public_key) { + } else if let Some(best_public_key) = bsrec.public_keys().first() { + if let Ok(best_node_id) = routing_table.generate_node_id(best_public_key) { if let Some(nr) = routing_table.lookup_node_ref(best_node_id).ok().flatten() { nr.get_peer_info(routing_domain) diff --git a/veilid-core/src/network_manager/bootstrap/txt_bootstrap/v0.rs b/veilid-core/src/network_manager/bootstrap/txt_bootstrap/v0.rs index 889bd764..c754aec7 100644 --- a/veilid-core/src/network_manager/bootstrap/txt_bootstrap/v0.rs +++ b/veilid-core/src/network_manager/bootstrap/txt_bootstrap/v0.rs @@ -104,7 +104,10 @@ impl NetworkManager { let mut mbi = 0; while mbi < merged_bootstrap_records.len() { let mbr = &mut merged_bootstrap_records[mbi]; - if mbr.public_keys().contains_any(bsrec.public_keys()) { + if mbr + .public_keys() + .contains_any_from_slice(bsrec.public_keys()) + { // Merge record, pop this one out let mbr = merged_bootstrap_records.remove(mbi); bsrec.merge(mbr); diff --git a/veilid-core/src/network_manager/bootstrap/txt_bootstrap/v1.rs b/veilid-core/src/network_manager/bootstrap/txt_bootstrap/v1.rs index be485968..36e5ae53 100644 --- a/veilid-core/src/network_manager/bootstrap/txt_bootstrap/v1.rs +++ b/veilid-core/src/network_manager/bootstrap/txt_bootstrap/v1.rs @@ -44,7 +44,10 @@ impl NetworkManager { let mut mbi = 0; while mbi < merged_bootstrap_records.len() { let mbr = &mut merged_bootstrap_records[mbi]; - if mbr.public_keys().contains_any(bsrec.public_keys()) { + if mbr + .public_keys() + .contains_any_from_slice(bsrec.public_keys()) + { // Merge record, pop this one out let mbr = merged_bootstrap_records.remove(mbi); bsrec.merge(mbr); diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index 1a47633c..65a1d54c 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -67,7 +67,7 @@ pub const TXT_LOOKUP_CACHE_SIZE: usize = 256; /// Duration that TXT lookups are valid in the cache (5 minutes, <= the DNS record expiration timeout) pub const TXT_LOOKUP_EXPIRATION: TimestampDuration = TimestampDuration::new_secs(300); /// Maximum size for a message is the same as the maximum size for an Envelope -pub const MAX_MESSAGE_SIZE: usize = MAX_ENVELOPE_SIZE; +pub const MAX_MESSAGE_SIZE: usize = ENV0_MAX_ENVELOPE_SIZE; /// Statistics table size for tracking performance by IP address pub const IPADDR_TABLE_SIZE: usize = 1024; /// Eviction time for ip addresses from statistics tables (5 minutes) @@ -111,8 +111,8 @@ impl SendDataResult { Some(ncm) if ncm.is_direct() ) } - pub fn is_ordered(&self) -> bool { - self.unique_flow.flow.protocol_type().is_ordered() + pub fn sequence_ordering(&self) -> SequenceOrdering { + self.unique_flow.flow.protocol_type().sequence_ordering() } pub fn unique_flow(&self) -> UniqueFlow { @@ -286,9 +286,11 @@ impl NetworkManager { Some( bcs.derive_shared_secret( network_key_password.as_bytes(), - &bcs.generate_hash(network_key_password.as_bytes()), + bcs.generate_hash(network_key_password.as_bytes()) + .ref_value(), ) - .expect("failed to derive network key"), + .expect("failed to derive network key") + .value(), ) } else { None @@ -593,6 +595,7 @@ impl NetworkManager { /// Generates a multi-shot/normal receipt #[instrument(level = "trace", skip(self, extra_data, callback))] + #[expect(dead_code)] pub fn generate_receipt>( &self, expiration_us: TimestampDuration, @@ -612,15 +615,19 @@ impl NetworkManager { let nonce = vcrypto.random_nonce(); let node_id = routing_table.node_id(vcrypto.kind()); - let secret_key = routing_table.secret_key(vcrypto.kind()).value(); + let secret_key = routing_table.secret_key(vcrypto.kind()); + + // Encode envelope + let version = best_receipt_version(); + let receipt = match version { + RECEIPT_VERSION_RCP0 => { + Receipt::try_new_rcp0(&crypto, node_id.kind(), nonce, node_id, extra_data)? + } + _ => { + bail!("unsupported receipt version: {:?}", version); + } + }; - let receipt = Receipt::try_new( - best_receipt_version(), - node_id.kind(), - nonce, - node_id.value(), - extra_data, - )?; let out = receipt .to_signed_data(&crypto, &secret_key) .wrap_err("failed to generate signed receipt")?; @@ -652,15 +659,19 @@ impl NetworkManager { let nonce = vcrypto.random_nonce(); let node_id = routing_table.node_id(vcrypto.kind()); - let secret_key = routing_table.secret_key(vcrypto.kind()).value(); + let secret_key = routing_table.secret_key(vcrypto.kind()); + + let version = best_receipt_version(); + + let receipt = match version { + RECEIPT_VERSION_RCP0 => { + Receipt::try_new_rcp0(&crypto, node_id.kind(), nonce, node_id, extra_data)? + } + _ => { + bail!("unsupported receipt version: {:?}", version); + } + }; - let receipt = Receipt::try_new( - best_receipt_version(), - node_id.kind(), - nonce, - node_id.value(), - extra_data, - )?; let out = receipt .to_signed_data(&crypto, &secret_key) .wrap_err("failed to generate signed receipt")?; @@ -687,7 +698,7 @@ impl NetworkManager { let receipt_manager = self.receipt_manager(); let crypto = self.crypto(); - let receipt = match Receipt::from_signed_data(&crypto, receipt_data.as_ref()) { + let receipt = match Receipt::try_from_signed_data(&crypto, receipt_data.as_ref()) { Err(e) => { return NetworkResult::invalid_message(e.to_string()); } @@ -713,7 +724,7 @@ impl NetworkManager { let receipt_manager = self.receipt_manager(); let crypto = self.crypto(); - let receipt = match Receipt::from_signed_data(&crypto, receipt_data.as_ref()) { + let receipt = match Receipt::try_from_signed_data(&crypto, receipt_data.as_ref()) { Err(e) => { return NetworkResult::invalid_message(e.to_string()); } @@ -738,7 +749,7 @@ impl NetworkManager { let receipt_manager = self.receipt_manager(); let crypto = self.crypto(); - let receipt = match Receipt::from_signed_data(&crypto, receipt_data.as_ref()) { + let receipt = match Receipt::try_from_signed_data(&crypto, receipt_data.as_ref()) { Err(e) => { return NetworkResult::invalid_message(e.to_string()); } @@ -755,7 +766,7 @@ impl NetworkManager { pub async fn handle_private_receipt>( &self, receipt_data: R, - private_route: BarePublicKey, + private_route: PublicKey, ) -> NetworkResult<()> { let Ok(_guard) = self.startup_context.startup_lock.enter() else { return NetworkResult::service_unavailable("network is not started"); @@ -764,7 +775,7 @@ impl NetworkManager { let receipt_manager = self.receipt_manager(); let crypto = self.crypto(); - let receipt = match Receipt::from_signed_data(&crypto, receipt_data.as_ref()) { + let receipt = match Receipt::try_from_signed_data(&crypto, receipt_data.as_ref()) { Err(e) => { return NetworkResult::invalid_message(e.to_string()); } @@ -805,9 +816,11 @@ impl NetworkManager { }; // Restrict reverse connection to same sequencing requirement as inbound signal - if signal_flow.protocol_type().is_ordered() { - peer_nr.set_sequencing(Sequencing::EnsureOrdered); - } + let sequencing = signal_flow + .protocol_type() + .sequence_ordering() + .strict_sequencing(); + peer_nr.set_sequencing(sequencing); // Make a reverse connection to the peer and send the receipt to it rpc.rpc_call_return_receipt(Destination::direct(peer_nr), receipt) @@ -886,21 +899,21 @@ impl NetworkManager { }; let node_id = routing_table.node_id(vcrypto.kind()); - let secret_key = routing_table.secret_key(vcrypto.kind()).value(); + let secret_key = routing_table.secret_key(vcrypto.kind()); // Get timestamp, nonce let ts = Timestamp::now(); let nonce = vcrypto.random_nonce(); // Encode envelope - let envelope = Envelope::new( - version, - node_id.kind(), - ts, - nonce, - node_id.value(), - dest_node_id.value(), - ); + let envelope = match version { + ENVELOPE_VERSION_ENV0 => { + Envelope::try_new_env0(&crypto, node_id.kind(), ts, nonce, node_id, dest_node_id)? + } + _ => { + bail!("unsupported envelope version: {:?}", version); + } + }; envelope .to_encrypted_data(&crypto, body.as_ref(), &secret_key, &self.network_key) .wrap_err("envelope failed to encode") @@ -1046,7 +1059,7 @@ impl NetworkManager { // Decode envelope header (may fail signature validation) let crypto = self.crypto(); - let envelope = match Envelope::from_signed_data(&crypto, data, &self.network_key) { + let envelope = match Envelope::try_from_signed_data(&crypto, data, &self.network_key) { Ok(v) => v, Err(e) => { veilid_log!(self debug "envelope failed to decode: {}", e); @@ -1164,9 +1177,8 @@ impl NetworkManager { if let Some((mut relay_nr, relay_kind)) = some_relay { // Ensure the protocol used to forward is of the same sequencing requirement // Address type is allowed to change if connectivity is better - if flow.protocol_type().is_ordered() { - relay_nr.set_sequencing(Sequencing::EnsureOrdered); - }; + let sequencing = flow.protocol_type().sequence_ordering().strict_sequencing(); + relay_nr.set_sequencing(sequencing); // Pass relay to RPC system if let Err(e) = self.enqueue_relay(relay_nr, data.to_vec(), relay_kind) { @@ -1180,7 +1192,7 @@ impl NetworkManager { } // DH to get decryption key (cached) - let secret_key = routing_table.secret_key(envelope.get_crypto_kind()).value(); + let secret_key = routing_table.secret_key(envelope.get_crypto_kind()); // Decrypt the envelope body let crypto = self.crypto(); @@ -1257,7 +1269,12 @@ impl NetworkManager { // Inform the connection table about the flow's priority let is_relaying_flow = node_ref.is_relaying(routing_domain); - if is_relaying_flow && flow.protocol_type().is_ordered() { + if is_relaying_flow + && matches!( + flow.protocol_type().sequence_ordering(), + SequenceOrdering::Ordered + ) + { self.connection_manager().add_relaying_flow(flow); } } diff --git a/veilid-core/src/network_manager/native/igd_manager.rs b/veilid-core/src/network_manager/native/igd_manager.rs index 559370e4..0f367e51 100644 --- a/veilid-core/src/network_manager/native/igd_manager.rs +++ b/veilid-core/src/network_manager/native/igd_manager.rs @@ -92,6 +92,7 @@ impl IGDManager { } #[instrument(level = "trace", target = "net", skip_all)] + #[expect(dead_code)] pub async fn unmap_port( &self, protocol_type: IGDProtocolType, diff --git a/veilid-core/src/network_manager/receipt_manager.rs b/veilid-core/src/network_manager/receipt_manager.rs index 3b30ff8f..fe5ac9d8 100644 --- a/veilid-core/src/network_manager/receipt_manager.rs +++ b/veilid-core/src/network_manager/receipt_manager.rs @@ -17,7 +17,7 @@ pub enum ReceiptEvent { ReturnedSafety, ReturnedPrivate { #[expect(dead_code)] - private_route: BarePublicKey, + private_route: PublicKey, }, Expired, Cancelled, @@ -28,7 +28,7 @@ pub(super) enum ReceiptReturned { OutOfBand, InBand { inbound_noderef: FilteredNodeRef }, Safety, - Private { private_route: BarePublicKey }, + Private { private_route: PublicKey }, } pub trait ReceiptCallback: Send + 'static { @@ -86,7 +86,6 @@ struct ReceiptRecord { } impl ReceiptRecord { - #[expect(dead_code)] pub fn new( receipt: Receipt, expiration_ts: Timestamp, @@ -145,7 +144,7 @@ impl PartialOrd for ReceiptRecordTimestampSort { /////////////////////////////////// struct ReceiptManagerInner { - records_by_nonce: BTreeMap>>, + records_by_nonce: BTreeMap>>, next_oldest_ts: Option, stop_source: Option, timeout_task: MustJoinSingleFuture<()>, @@ -405,7 +404,7 @@ impl ReceiptManager { } #[expect(dead_code)] - pub async fn cancel_receipt(&self, nonce: &BareNonce) -> EyreResult<()> { + pub async fn cancel_receipt(&self, nonce: &Nonce) -> EyreResult<()> { event!(target: "receipt", Level::DEBUG, "== Cancel Receipt {}", nonce.encode()); let _guard = self.unlocked_inner.startup_lock.enter()?; diff --git a/veilid-core/src/network_manager/send_data.rs b/veilid-core/src/network_manager/send_data.rs index 6a5575fb..a2c873a8 100644 --- a/veilid-core/src/network_manager/send_data.rs +++ b/veilid-core/src/network_manager/send_data.rs @@ -691,13 +691,13 @@ impl NetworkManager { relay_nr.set_sequencing(sequencing); // Tighten sequencing for the target to the best reverse connection flow we can get - let tighten = peer_a + let max_ordering = peer_a .node_info() .filtered_dial_info_details(DialInfoDetail::NO_SORT, &|did| { did.matches_filter(&dial_info_filter) }) .iter() - .find_map(|did| { + .fold(SequenceOrdering::Unordered, |ord, did| { if peer_b .node_info() .address_types() @@ -706,21 +706,17 @@ impl NetworkManager { .node_info() .outbound_protocols() .contains(did.dial_info.protocol_type()) - && did.dial_info.protocol_type().is_ordered() { - Some(true) + cmp::max(ord, did.dial_info.protocol_type().sequence_ordering()) } else { - None + ord } - }) - .unwrap_or(false); + }); let mut target_node_ref = target_node_ref.filtered_clone( NodeRefFilter::from(dial_info_filter).with_routing_domain(routing_domain), ); - if tighten { - target_node_ref.set_sequencing(Sequencing::EnsureOrdered); - } + target_node_ref.set_sequencing(max_ordering.strict_sequencing()); Some(NodeContactMethodKind::SignalReverse( relay_nr, target_node_ref, diff --git a/veilid-core/src/network_manager/tests/test_signed_node_info.rs b/veilid-core/src/network_manager/tests/test_signed_node_info.rs index 2755f167..b23dedba 100644 --- a/veilid-core/src/network_manager/tests/test_signed_node_info.rs +++ b/veilid-core/src/network_manager/tests/test_signed_node_info.rs @@ -15,14 +15,14 @@ pub async fn test_signed_node_info() { for ck in VALID_CRYPTO_KINDS { let vcrypto = crypto.get(ck).unwrap(); let keypair = vcrypto.generate_keypair(); - let secret_key_group = SecretKeyGroup::from(SecretKey::new(ck, keypair.secret())); + let secret_key_group = SecretKeyGroup::from(keypair.secret()); // Build test node info let node_info = NodeInfo::new( Timestamp::now(), VALID_ENVELOPE_VERSIONS.to_vec(), vec![CryptoInfo::VLD0 { - public_key: keypair.key(), + public_key: keypair.key().value(), }], PUBLIC_INTERNET_CAPABILITIES.to_vec(), ProtocolTypeSet::all(), @@ -82,7 +82,7 @@ pub async fn test_signed_node_info() { .expect_err("should not validate"); let invalid_crypto_kind = SignatureGroup::from(Signature::new( - CryptoKind(*b"FOOO"), + CryptoKind::new(*b"FOOO"), BareSignature::default(), )); diff --git a/veilid-core/src/network_manager/types/dial_info_filter.rs b/veilid-core/src/network_manager/types/dial_info_filter.rs index 3a312511..232a378f 100644 --- a/veilid-core/src/network_manager/types/dial_info_filter.rs +++ b/veilid-core/src/network_manager/types/dial_info_filter.rs @@ -62,7 +62,7 @@ impl DialInfoFilter { } pub fn is_ordered_only(&self) -> bool { for pt in self.protocol_type_set { - if !pt.is_ordered() { + if !matches!(pt.sequence_ordering(), SequenceOrdering::Ordered) { return false; } } diff --git a/veilid-core/src/network_manager/types/flow.rs b/veilid-core/src/network_manager/types/flow.rs index a3328bf8..beb0c082 100644 --- a/veilid-core/src/network_manager/types/flow.rs +++ b/veilid-core/src/network_manager/types/flow.rs @@ -29,7 +29,12 @@ impl fmt::Display for Flow { impl Flow { pub fn new(remote: PeerAddress, local: SocketAddress) -> Self { - assert!(!remote.protocol_type().is_ordered() || !local.address().is_unspecified()); + assert!( + !matches!( + remote.protocol_type().sequence_ordering(), + SequenceOrdering::Ordered + ) || !local.address().is_unspecified() + ); Self { remote, diff --git a/veilid-core/src/network_manager/types/protocol_type.rs b/veilid-core/src/network_manager/types/protocol_type.rs index 6b740059..5125594e 100644 --- a/veilid-core/src/network_manager/types/protocol_type.rs +++ b/veilid-core/src/network_manager/types/protocol_type.rs @@ -14,28 +14,12 @@ pub(crate) enum ProtocolType { } impl ProtocolType { - pub fn is_ordered(&self) -> bool { - matches!( - self, - ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS - ) - } - #[expect(dead_code)] - pub fn minimum_sequencing(&self) -> Sequencing { + pub fn sequence_ordering(&self) -> SequenceOrdering { match self { - ProtocolType::UDP => Sequencing::NoPreference, - ProtocolType::TCP => Sequencing::PreferOrdered, - ProtocolType::WS => Sequencing::PreferOrdered, - ProtocolType::WSS => Sequencing::PreferOrdered, - } - } - #[expect(dead_code)] - pub fn maximum_sequencing(&self) -> Sequencing { - match self { - ProtocolType::UDP => Sequencing::PreferOrdered, - ProtocolType::TCP => Sequencing::EnsureOrdered, - ProtocolType::WS => Sequencing::EnsureOrdered, - ProtocolType::WSS => Sequencing::EnsureOrdered, + ProtocolType::UDP => SequenceOrdering::Unordered, + ProtocolType::TCP => SequenceOrdering::Ordered, + ProtocolType::WS => SequenceOrdering::Ordered, + ProtocolType::WSS => SequenceOrdering::Ordered, } } diff --git a/veilid-core/src/network_manager/types/signal_info.rs b/veilid-core/src/network_manager/types/signal_info.rs index 9645c5c2..fbf5a19a 100644 --- a/veilid-core/src/network_manager/types/signal_info.rs +++ b/veilid-core/src/network_manager/types/signal_info.rs @@ -27,10 +27,10 @@ impl SignalInfo { receipt, peer_info: _, } => { - if receipt.len() < MIN_RECEIPT_SIZE { + if receipt.len() < RCP0_MIN_RECEIPT_SIZE { return Err(RPCError::protocol("SignalInfo HolePunch receipt too short")); } - if receipt.len() > MAX_RECEIPT_SIZE { + if receipt.len() > RCP0_MAX_RECEIPT_SIZE { return Err(RPCError::protocol("SignalInfo HolePunch receipt too long")); } Ok(()) @@ -39,12 +39,12 @@ impl SignalInfo { receipt, peer_info: _, } => { - if receipt.len() < MIN_RECEIPT_SIZE { + if receipt.len() < RCP0_MIN_RECEIPT_SIZE { return Err(RPCError::protocol( "SignalInfo ReverseConnect receipt too short", )); } - if receipt.len() > MAX_RECEIPT_SIZE { + if receipt.len() > RCP0_MAX_RECEIPT_SIZE { return Err(RPCError::protocol( "SignalInfo ReverseConnect receipt too long", )); diff --git a/veilid-core/src/routing_table/bucket.rs b/veilid-core/src/routing_table/bucket.rs index 4c93f4b9..b0dc6427 100644 --- a/veilid-core/src/routing_table/bucket.rs +++ b/veilid-core/src/routing_table/bucket.rs @@ -5,7 +5,7 @@ impl_veilid_log_facility!("rtab"); /// Routing Table Bucket /// Stores map of public keys to entries, which may be in multiple routing tables per crypto kind -/// Keeps entries at a particular 'dht distance' from this cryptokind's node id +/// Keeps entries at a particular 'hash coordinate distance' from this cryptokind's node id /// Helps to keep managed lists at particular distances so we can evict nodes by priority /// where the priority comes from liveness and age of the entry (older is better) pub struct Bucket { diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index f3263343..39a1b9ae 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -162,10 +162,8 @@ impl fmt::Display for BucketEntryLocalNetwork { /// The data associated with each bucket entry #[derive(Debug, Serialize, Deserialize)] pub(crate) struct BucketEntryInner { - /// The node ids matching this bucket entry, with the cryptography versions supported by this node as the 'kind' field - validated_node_ids: NodeIdGroup, - /// The node ids claimed by the remote node that use cryptography versions we do not support - unsupported_node_ids: NodeIdGroup, + /// The node ids matching this bucket entry + node_ids: NodeIdGroup, /// The set of envelope versions supported by the node inclusive of the requirements of any relay the node may be using envelope_support: Vec, /// If this node has updated it's SignedNodeInfo since our network @@ -220,8 +218,7 @@ pub(crate) struct BucketEntryInner { impl fmt::Display for BucketEntryInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "validated_node_ids: {}", self.validated_node_ids)?; - writeln!(f, "unsupported_node_ids: {}", self.unsupported_node_ids)?; + writeln!(f, "node_ids: {}", self.node_ids)?; writeln!(f, "envelope_support: {:?}", self.envelope_support)?; writeln!( f, @@ -281,9 +278,46 @@ impl BucketEntryInner { /// Get all node ids pub fn node_ids(&self) -> NodeIdGroup { - let mut node_ids = self.validated_node_ids.clone(); - node_ids.add_all(&self.unsupported_node_ids); - node_ids + self.node_ids.clone() + } + + /// Get public keys + pub fn public_keys(&self, routing_domain: RoutingDomain) -> PublicKeyGroup { + match routing_domain { + RoutingDomain::LocalNetwork => self + .local_network + .peer_info + .as_ref() + .map(|x| x.node_info().public_keys()) + .unwrap_or_default(), + RoutingDomain::PublicInternet => self + .public_internet + .peer_info + .as_ref() + .map(|x| x.node_info().public_keys()) + .unwrap_or_default(), + } + } + + /// Get best node id + pub fn best_node_id(&self) -> Option { + self.node_ids.first().cloned() + } + + /// Get best public key + pub fn best_public_key(&self, routing_domain: RoutingDomain) -> Option { + match routing_domain { + RoutingDomain::LocalNetwork => self + .local_network + .peer_info + .as_ref() + .and_then(|x| x.node_info().public_keys().first().cloned()), + RoutingDomain::PublicInternet => self + .public_internet + .peer_info + .as_ref() + .and_then(|x| x.node_info().public_keys().first().cloned()), + } } /// Add a node id for a particular crypto kind. @@ -291,28 +325,17 @@ impl BucketEntryInner { /// Returns Ok(None) if no previous existing node id was associated with that crypto kind, or one existed but nothing changed. /// Results Err() if this operation would add more crypto kinds than we support pub fn add_node_id(&mut self, node_id: NodeId) -> EyreResult> { - let total_node_id_count = self.validated_node_ids.len() + self.unsupported_node_ids.len(); - let node_ids = if VALID_CRYPTO_KINDS.contains(&node_id.kind()) { - &mut self.validated_node_ids - } else { - &mut self.unsupported_node_ids - }; - - if let Some(old_node_id) = node_ids.get(node_id.kind()) { + if let Some(old_node_id) = self.node_ids.get(node_id.kind()) { // If this was already there we do nothing if old_node_id == node_id { return Ok(None); } // Won't change number of crypto kinds, but the node id changed - node_ids.add(node_id); + self.node_ids.add(node_id); return Ok(Some(old_node_id)); } - // Check to ensure we aren't adding more crypto kinds than we support - if total_node_id_count == MAX_CRYPTO_KINDS { - bail!("too many crypto kinds for this node"); - } - node_ids.add(node_id); + self.node_ids.add(node_id); Ok(None) } @@ -321,17 +344,7 @@ impl BucketEntryInner { /// Returns Some(node) any previous existing node id associated with that crypto kind /// Returns None if no previous existing node id was associated with that crypto kind pub fn remove_node_id(&mut self, crypto_kind: CryptoKind) -> Option { - let node_ids = if VALID_CRYPTO_KINDS.contains(&crypto_kind) { - &mut self.validated_node_ids - } else { - &mut self.unsupported_node_ids - }; - - node_ids.remove(crypto_kind) - } - - pub fn best_node_id(&self) -> Option { - self.validated_node_ids.best() + self.node_ids.remove(crypto_kind) } pub fn relay_ids(&self, routing_domain: RoutingDomain) -> Vec { @@ -354,11 +367,11 @@ impl BucketEntryInner { /// Get crypto kinds pub fn crypto_kinds(&self) -> Vec { - self.validated_node_ids.kinds() + self.node_ids.kinds() } /// Compare sets of crypto kinds pub fn common_crypto_kinds(&self, other: &[CryptoKind]) -> Vec { - common_crypto_kinds(&self.validated_node_ids.kinds(), other) + common_crypto_kinds(&self.node_ids.kinds(), other) } /// All-of capability check @@ -699,7 +712,7 @@ impl BucketEntryInner { // Check if the connection is still considered live let alive = // Should we check the connection table? - if v.0.protocol_type().is_ordered() { + if matches!(v.0.protocol_type().sequence_ordering(), SequenceOrdering::Ordered) { // Look the connection up in the connection manager and see if it's still there if let Some(connection_manager) = &opt_connection_manager { connection_manager.get_connection(v.0).is_some() @@ -1095,13 +1108,16 @@ impl BucketEntryInner { ts: Timestamp, bytes: ByteCount, expects_answer: bool, - ordered: bool, + ordering: SequenceOrdering, ) { self.transfer_stats_accounting.add_up(bytes); - if ordered { - self.answer_stats_accounting_ordered.record_question(ts); - } else { - self.answer_stats_accounting_unordered.record_question(ts); + match ordering { + SequenceOrdering::Ordered => { + self.answer_stats_accounting_ordered.record_question(ts); + } + SequenceOrdering::Unordered => { + self.answer_stats_accounting_unordered.record_question(ts); + } } self.peer_stats.rpc_stats.messages_sent += 1; self.peer_stats.rpc_stats.failed_to_send = 0; @@ -1125,43 +1141,53 @@ impl BucketEntryInner { send_ts: Timestamp, recv_ts: Timestamp, bytes: ByteCount, - ordered: bool, + ordering: SequenceOrdering, ) { self.transfer_stats_accounting.add_down(bytes); - if ordered { - self.answer_stats_accounting_ordered.record_answer(recv_ts); - self.peer_stats.rpc_stats.recent_lost_answers_ordered = 0; - } else { - self.answer_stats_accounting_unordered - .record_answer(recv_ts); - self.peer_stats.rpc_stats.recent_lost_answers_unordered = 0; + + match ordering { + SequenceOrdering::Ordered => { + self.answer_stats_accounting_ordered.record_answer(recv_ts); + self.peer_stats.rpc_stats.recent_lost_answers_ordered = 0; + } + SequenceOrdering::Unordered => { + self.answer_stats_accounting_unordered + .record_answer(recv_ts); + self.peer_stats.rpc_stats.recent_lost_answers_unordered = 0; + } } + self.peer_stats.rpc_stats.messages_rcvd += 1; self.peer_stats.rpc_stats.questions_in_flight -= 1; self.record_latency(recv_ts.saturating_sub(send_ts)); self.touch_last_seen(recv_ts); } - pub(super) fn lost_answer(&mut self, ordered: bool) { + pub(super) fn lost_answer(&mut self, ordering: SequenceOrdering) { let cur_ts = Timestamp::now(); - if ordered { - self.answer_stats_accounting_ordered - .record_lost_answer(cur_ts); - self.peer_stats.rpc_stats.recent_lost_answers_ordered += 1; - if self.peer_stats.rpc_stats.recent_lost_answers_ordered - > UNRELIABLE_LOST_ANSWERS_ORDERED - { - self.peer_stats.rpc_stats.first_consecutive_seen_ts = None; + + match ordering { + SequenceOrdering::Ordered => { + self.answer_stats_accounting_ordered + .record_lost_answer(cur_ts); + self.peer_stats.rpc_stats.recent_lost_answers_ordered += 1; + if self.peer_stats.rpc_stats.recent_lost_answers_ordered + > UNRELIABLE_LOST_ANSWERS_ORDERED + { + self.peer_stats.rpc_stats.first_consecutive_seen_ts = None; + } } - } else { - self.answer_stats_accounting_unordered - .record_lost_answer(cur_ts); - self.peer_stats.rpc_stats.recent_lost_answers_unordered += 1; - if self.peer_stats.rpc_stats.recent_lost_answers_unordered - > UNRELIABLE_LOST_ANSWERS_UNORDERED - { - self.peer_stats.rpc_stats.first_consecutive_seen_ts = None; + SequenceOrdering::Unordered => { + self.answer_stats_accounting_unordered + .record_lost_answer(cur_ts); + self.peer_stats.rpc_stats.recent_lost_answers_unordered += 1; + if self.peer_stats.rpc_stats.recent_lost_answers_unordered + > UNRELIABLE_LOST_ANSWERS_UNORDERED + { + self.peer_stats.rpc_stats.first_consecutive_seen_ts = None; + } } } + self.peer_stats.rpc_stats.questions_in_flight -= 1; } pub(super) fn failed_to_send(&mut self, ts: Timestamp, expects_answer: bool) { @@ -1199,8 +1225,7 @@ impl BucketEntry { let now = Timestamp::now(); let inner = BucketEntryInner { - validated_node_ids: NodeIdGroup::from(first_node_id), - unsupported_node_ids: NodeIdGroup::new(), + node_ids: NodeIdGroup::from(first_node_id), envelope_support: Vec::new(), updated_since_last_network_change: false, last_flows: BTreeMap::new(), diff --git a/veilid-core/src/routing_table/debug.rs b/veilid-core/src/routing_table/debug.rs index 3a3c3df6..8d917d08 100644 --- a/veilid-core/src/routing_table/debug.rs +++ b/veilid-core/src/routing_table/debug.rs @@ -339,7 +339,7 @@ impl RoutingTable { let is_relaying = node .operate(|_rti, e| { e.node_info(RoutingDomain::PublicInternet) - .map(|ni| our_node_ids.contains_any(&ni.relay_ids())) + .map(|ni| our_node_ids.contains_any_from_slice(&ni.relay_ids())) }) .unwrap_or(false); diff --git a/veilid-core/src/routing_table/find_peers.rs b/veilid-core/src/routing_table/find_peers.rs index 17e96602..53f9d41c 100644 --- a/veilid-core/src/routing_table/find_peers.rs +++ b/veilid-core/src/routing_table/find_peers.rs @@ -8,7 +8,7 @@ impl RoutingTable { pub fn find_preferred_closest_peers( &self, routing_domain: RoutingDomain, - hash_coordinate: &HashDigest, + hash_coordinate: HashCoordinate, capabilities: &[VeilidCapability], ) -> NetworkResult>> { if Crypto::validate_crypto_kind(hash_coordinate.kind()).is_err() { @@ -71,7 +71,7 @@ impl RoutingTable { pub fn find_preferred_peers_closer_to_key( &self, routing_domain: RoutingDomain, - hash_coordinate: &HashDigest, + hash_coordinate: HashCoordinate, required_capabilities: Vec, ) -> NetworkResult>> { // add node information for the requesting node to our routing table @@ -80,18 +80,9 @@ impl RoutingTable { // find N nodes closest to the target node in our routing table // ensure the nodes returned are only the ones closer to the target node than ourself - let crypto = self.crypto(); - let Some(vcrypto) = crypto.get(crypto_kind) else { - return NetworkResult::invalid_message("unsupported cryptosystem"); - }; - let vcrypto = &vcrypto; + let own_distance = own_node_id.to_hash_coordinate().distance(&hash_coordinate); - let own_distance = vcrypto.distance( - &BareHashDigest::from(own_node_id.value()), - &hash_coordinate.value(), - ); - - let value = hash_coordinate.value(); + let hash_coordinate2 = hash_coordinate.clone(); let filter = Box::new( move |_rti: &RoutingTableInner, opt_entry: Option>| { // Exclude our own node @@ -112,8 +103,9 @@ impl RoutingTable { let Some(entry_node_id) = e.node_ids().get(crypto_kind) else { return false; }; - let entry_distance = vcrypto - .distance(&BareHashDigest::from(entry_node_id.value()), &value.clone()); + let entry_distance = entry_node_id + .to_hash_coordinate() + .distance(&hash_coordinate2); if entry_distance >= own_distance { return false; } @@ -151,10 +143,9 @@ impl RoutingTable { // Validate peers returned are, in fact, closer to the key than the node we sent this to // This same test is used on the other side so we vet things here - let valid = match Self::verify_peers_closer( - vcrypto, - &own_node_id.clone().into(), - &hash_coordinate.clone(), + let valid = match self.verify_peers_closer( + own_node_id.to_hash_coordinate(), + hash_coordinate.clone(), &closest_nodes, ) { Ok(v) => v, @@ -175,24 +166,22 @@ impl RoutingTable { /// Determine if set of peers is closer to key_near than key_far is to key_near #[instrument(level = "trace", target = "rtab", skip_all, err)] pub fn verify_peers_closer( - vcrypto: &crypto::CryptoSystemGuard<'_>, - key_far: &HashDigest, - key_near: &HashDigest, + &self, + hash_coordinate_far: HashCoordinate, + hash_coordinate_near: HashCoordinate, peers: &[Arc], ) -> EyreResult { - let kind = vcrypto.kind(); - - if key_far.kind() != kind || key_near.kind() != kind { + if hash_coordinate_far.kind() != hash_coordinate_near.kind() { bail!("keys all need the same cryptosystem"); } let mut closer = true; - let d_far = vcrypto.distance(key_far.ref_value(), key_near.ref_value()); + let d_far = hash_coordinate_far.distance(&hash_coordinate_near); for peer in peers { - let Some(key_peer) = peer.node_ids().get(kind) else { + let Some(key_peer) = peer.node_ids().get(hash_coordinate_far.kind()) else { bail!("peers need to have a key with the same cryptosystem"); }; - let d_near = vcrypto.distance(key_near.ref_value(), &key_peer.value().into()); + let d_near = hash_coordinate_near.distance(&key_peer.to_hash_coordinate()); if d_far < d_near { let warning = format!( r#"peer: {} @@ -202,14 +191,13 @@ far (self): {} d_far: {} cmp: {:?}"#, key_peer, - key_near, - key_far, + hash_coordinate_near, + hash_coordinate_far, d_near, d_far, d_near.cmp(&d_far) ); - let crypto = vcrypto.crypto(); - veilid_log!(crypto warn "{}", warning); + veilid_log!(self warn "{}", warning); closer = false; break; } diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index eef8a585..23090f0a 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -36,9 +36,7 @@ impl_veilid_log_facility!("rtab"); ////////////////////////////////////////////////////////////////////////// /// Routing table bucket count (one per bit per 32 byte node id) -pub const BUCKET_COUNT: usize = 256; -/// Fixed length for NodeId in bytes -pub const NODE_ID_LENGTH: usize = 32; +pub const BUCKET_COUNT: usize = HASH_COORDINATE_LENGTH * 8; /// Minimum number of nodes we need, per crypto kind, per routing domain, or we trigger a bootstrap pub const MIN_BOOTSTRAP_CONNECTIVITY_PEERS: usize = 4; @@ -375,18 +373,9 @@ impl RoutingTable { false } - pub fn matches_own_node_id_key(&self, node_id_key: &BareNodeId) -> bool { - for tk in self.node_ids().iter() { - if tk.ref_value() == node_id_key { - return true; - } - } - false - } - /// Produce node id from public key pub fn generate_node_id(&self, public_key: &PublicKey) -> VeilidAPIResult { - if public_key.ref_value().len() == NODE_ID_LENGTH { + if public_key.ref_value().len() == HASH_COORDINATE_LENGTH { return Ok(NodeId::new( public_key.kind(), BareNodeId::new(public_key.ref_value()), @@ -399,13 +388,13 @@ impl RoutingTable { let idhash = vcrypto.generate_hash(public_key.ref_value()); assert!( - idhash.len() >= NODE_ID_LENGTH, + idhash.ref_value().len() >= HASH_COORDINATE_LENGTH, "generate_hash needs to produce at least {} bytes", - NODE_ID_LENGTH + HASH_COORDINATE_LENGTH ); Ok(NodeId::new( public_key.kind(), - BareNodeId::new(&idhash[0..NODE_ID_LENGTH]), + BareNodeId::new(&idhash.ref_value()[0..HASH_COORDINATE_LENGTH]), )) } @@ -413,16 +402,12 @@ impl RoutingTable { if node_id.ref_value().len() * 8 != BUCKET_COUNT { bail!("NodeId should be hashed down to BUCKET_COUNT bits"); } - let crypto = self.crypto(); - let self_node_id_key = self.node_id(node_id.kind()).value(); - let vcrypto = crypto.get(node_id.kind()).unwrap(); + let self_hash_coordinate = self.node_id(node_id.kind()).to_hash_coordinate(); Ok(( node_id.kind(), - vcrypto - .distance( - &BareHashDigest::from(node_id.value()), - &BareHashDigest::from(self_node_id_key), - ) + node_id + .to_hash_coordinate() + .distance(&self_hash_coordinate) .first_nonzero_bit() .unwrap(), )) @@ -615,6 +600,7 @@ impl RoutingTable { self.inner.read().dial_info_details(domain) } + #[expect(dead_code)] pub fn all_filtered_dial_info_details( &self, routing_domain_set: RoutingDomainSet, @@ -676,6 +662,7 @@ impl RoutingTable { } /// Return a list of the current valid bootstrap peers in a particular routing domain + #[expect(dead_code)] pub fn get_bootstrap_peers(&self, routing_domain: RoutingDomain) -> Vec { self.inner.read().get_bootstrap_peers(routing_domain) } @@ -932,26 +919,30 @@ impl RoutingTable { pub fn find_preferred_closest_nodes<'a, T, O>( &self, node_count: usize, - node_id: HashDigest, + hash_coordinate: HashCoordinate, filters: VecDeque, transform: T, ) -> VeilidAPIResult> where T: for<'r> FnMut(&'r RoutingTableInner, Option>) -> O + Send, { - self.inner - .read() - .find_preferred_closest_nodes(node_count, node_id, filters, transform) + self.inner.read().find_preferred_closest_nodes( + node_count, + hash_coordinate, + filters, + transform, + ) } + #[expect(dead_code)] pub fn sort_and_clean_closest_noderefs( &self, - node_id: HashDigest, + hash_coordinate: HashCoordinate, closest_nodes: &[NodeRef], ) -> Vec { self.inner .read() - .sort_and_clean_closest_noderefs(node_id, closest_nodes) + .sort_and_clean_closest_noderefs(hash_coordinate, closest_nodes) } #[instrument(level = "trace", skip(self, peer_info_list))] @@ -1076,6 +1067,7 @@ impl RoutingTable { } #[instrument(level = "trace", skip(self, filter, metric), ret)] + #[expect(dead_code)] pub fn find_fastest_node( &self, cur_ts: Timestamp, @@ -1099,6 +1091,7 @@ impl RoutingTable { } #[instrument(level = "trace", skip(self, filter, metric), ret)] + #[expect(dead_code)] pub fn get_node_speed_percentile( &self, node_id: NodeId, diff --git a/veilid-core/src/routing_table/node_ref/node_ref_lock.rs b/veilid-core/src/routing_table/node_ref/node_ref_lock.rs index 19cc3e09..f15df6e5 100644 --- a/veilid-core/src/routing_table/node_ref/node_ref_lock.rs +++ b/veilid-core/src/routing_table/node_ref/node_ref_lock.rs @@ -39,7 +39,6 @@ impl<'a, N: NodeRefAccessorsTrait + NodeRefOperateTrait + fmt::Debug + fmt::Disp } } - #[expect(dead_code)] pub fn unlocked(&self) -> N { self.nr.clone() } diff --git a/veilid-core/src/routing_table/node_ref/traits.rs b/veilid-core/src/routing_table/node_ref/traits.rs index 2d1ed504..7a3f74ec 100644 --- a/veilid-core/src/routing_table/node_ref/traits.rs +++ b/veilid-core/src/routing_table/node_ref/traits.rs @@ -50,9 +50,15 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait fn node_ids(&self) -> NodeIdGroup { self.operate(|_rti, e| e.node_ids()) } + fn public_keys(&self, routing_domain: RoutingDomain) -> PublicKeyGroup { + self.operate(|_rti, e| e.public_keys(routing_domain)) + } fn best_node_id(&self) -> Option { self.operate(|_rti, e| e.best_node_id()) } + fn best_public_key(&self, routing_domain: RoutingDomain) -> Option { + self.operate(|_rti, e| e.best_public_key(routing_domain)) + } fn relay_ids(&self, routing_domain: RoutingDomain) -> Vec { self.operate(|_rti, e| e.relay_ids(routing_domain)) @@ -254,7 +260,7 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait return false; }; let our_node_ids = rti.routing_table().node_ids(); - our_node_ids.contains_any(relay_ids.as_slice()) + our_node_ids.contains_any_from_slice(relay_ids.as_slice()) }) } @@ -284,11 +290,11 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait ts: Timestamp, bytes: ByteCount, expects_answer: bool, - ordered: bool, + ordering: SequenceOrdering, ) { self.operate_mut(|rti, e| { rti.transfer_stats_accounting().add_up(bytes); - e.question_sent(ts, bytes, expects_answer, ordered); + e.question_sent(ts, bytes, expects_answer, ordering); }) } fn stats_question_rcvd(&self, ts: Timestamp, bytes: ByteCount) { @@ -308,18 +314,18 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait send_ts: Timestamp, recv_ts: Timestamp, bytes: ByteCount, - ordered: bool, + ordering: SequenceOrdering, ) { self.operate_mut(|rti, e| { rti.transfer_stats_accounting().add_down(bytes); rti.latency_stats_accounting() .record_latency(recv_ts.saturating_sub(send_ts)); - e.answer_rcvd(send_ts, recv_ts, bytes, ordered); + e.answer_rcvd(send_ts, recv_ts, bytes, ordering); }) } - fn stats_lost_answer(&self, ordered: bool) { + fn stats_lost_answer(&self, ordering: SequenceOrdering) { self.operate_mut(|_rti, e| { - e.lost_answer(ordered); + e.lost_answer(ordering); }) } fn stats_failed_to_send(&self, ts: Timestamp, expects_answer: bool) { diff --git a/veilid-core/src/routing_table/privacy.rs b/veilid-core/src/routing_table/privacy.rs index efe2a529..789cd4f9 100644 --- a/veilid-core/src/routing_table/privacy.rs +++ b/veilid-core/src/routing_table/privacy.rs @@ -8,7 +8,7 @@ impl_veilid_log_facility!("rtab"); #[derive(Clone)] pub(crate) struct RouteHopData { /// The nonce used in the encryption ENC(Xn,DH(PKn,SKapr)) - pub nonce: BareNonce, + pub nonce: Nonce, /// The encrypted blob pub blob: Vec, } @@ -62,7 +62,7 @@ impl RouteNode { RouteNode::NodeId(id) => { format!("{}", id) } - RouteNode::PeerInfo(pi) => match pi.node_ids().best() { + RouteNode::PeerInfo(pi) => match pi.node_ids().first() { Some(id) => format!("{}", id), None => { format!("?({})", pi.node_ids()) diff --git a/veilid-core/src/routing_table/route_spec_store/mod.rs b/veilid-core/src/routing_table/route_spec_store/mod.rs index 19dfa227..02a4dcc1 100644 --- a/veilid-core/src/routing_table/route_spec_store/mod.rs +++ b/veilid-core/src/routing_table/route_spec_store/mod.rs @@ -272,7 +272,7 @@ impl RouteSpecStore { } // Exclude nodes we have specifically chosen to avoid - if e.node_ids().contains_any(avoid_nodes) { + if e.node_ids().contains_any_from_slice(avoid_nodes) { return false; } @@ -375,11 +375,11 @@ impl RouteSpecStore { // Relay check for their_relay_info in their_ni.relay_info_list() { // Exclude nodes whose relays we have chosen to avoid - if their_relay_info.node_ids().contains_any(avoid_nodes) { + if their_relay_info.node_ids().contains_any_from_slice(avoid_nodes) { return false; } // Exclude nodes whose relay is our own relay if we have one - if their_relay_info.node_ids().contains_any(&own_relay_ids) { + if their_relay_info.node_ids().contains_any_from_slice(&own_relay_ids) { return false; } } @@ -535,7 +535,7 @@ impl RouteSpecStore { } // Ensure this route is viable by checking that each node can contact the next one - let mut can_do_sequenced = true; + let mut orderings = SequenceOrderingSet::all(); if directions.contains(Direction::Outbound) { let mut previous_node = published_peer_info.clone(); let mut reachable = true; @@ -554,18 +554,18 @@ impl RouteSpecStore { break; } - // Check if we can do sequenced specifically - if can_do_sequenced { + // Check if we can do each ordering strictly + for ordering in orderings { let cm = rti.get_contact_method( RoutingDomain::PublicInternet, previous_node.clone(), current_node.clone(), DialInfoFilter::all(), - Sequencing::EnsureOrdered, + ordering.strict_sequencing(), None, ); if matches!(cm, ContactMethod::Unreachable) { - can_do_sequenced = false; + orderings.remove(ordering); } } @@ -593,18 +593,18 @@ impl RouteSpecStore { break; } - // Check if we can do sequenced specifically - if can_do_sequenced { + // Check if we can do each ordering strictly + for ordering in orderings { let cm = rti.get_contact_method( RoutingDomain::PublicInternet, next_node.clone(), current_node.clone(), DialInfoFilter::all(), - Sequencing::EnsureOrdered, + ordering.strict_sequencing(), None, ); if matches!(cm, ContactMethod::Unreachable) { - can_do_sequenced = false; + orderings.remove(ordering); } } next_node = current_node; @@ -615,19 +615,19 @@ impl RouteSpecStore { } // Keep this route let route_nodes = permutation.to_vec(); - Some((route_nodes, can_do_sequenced)) + Some((route_nodes, orderings)) }) as PermFunc; let mut route_nodes: Vec = Vec::new(); - let mut can_do_sequenced: bool = true; + let mut orderings = SequenceOrderingSet::new(); for start in 0..(nodes.len() - safety_spec.hop_count) { // Try the permutations available starting with 'start' - if let Some((rn, cds)) = + if let Some((rn, ord)) = with_route_permutations(safety_spec.hop_count, start, &mut perm_func) { route_nodes = rn; - can_do_sequenced = cds; + orderings = ord; break; } } @@ -639,7 +639,7 @@ impl RouteSpecStore { // Got a unique route, lets build the details, register it, and return it let hop_node_refs: Vec = route_nodes.iter().map(|k| nodes[*k].clone()).collect(); - let mut route_set = BTreeMap::::new(); + let mut route_set = BTreeMap::::new(); let crypto = self.crypto(); for crypto_kind in crypto_kinds.iter().copied() { let vcrypto = crypto.get(crypto_kind).unwrap(); @@ -652,7 +652,6 @@ impl RouteSpecStore { route_set.insert( keypair.key(), RouteSpecDetail { - crypto_kind, secret_key: keypair.secret(), hops, }, @@ -665,7 +664,7 @@ impl RouteSpecStore { hop_node_refs, directions, safety_spec.stability, - can_do_sequenced, + orderings, automatic, ); @@ -697,12 +696,8 @@ impl RouteSpecStore { { let inner = &*self.inner.lock(); let crypto = self.crypto(); - let Some(vcrypto) = crypto.get(public_key.kind()) else { - veilid_log!(self debug "can't handle route with public key: {:?}", public_key); - return None; - }; - let Some(rsid) = inner.content.get_id_by_key(public_key.ref_value()) else { + let Some(rsid) = inner.content.get_id_by_key(public_key) else { veilid_log!(self debug target: "network_result", "route id does not exist: {:?}", public_key.ref_value()); return None; }; @@ -710,7 +705,7 @@ impl RouteSpecStore { veilid_log!(self debug "route detail does not exist: {:?}", rsid); return None; }; - let Some(rsd) = rssd.get_route_by_key(public_key.ref_value()) else { + let Some(rsd) = rssd.get_route_by_key(public_key) else { veilid_log!(self debug "route set {:?} does not have key: {:?}", rsid, public_key.ref_value()); return None; }; @@ -723,28 +718,35 @@ impl RouteSpecStore { } // Validate signatures to ensure the route was handled by the nodes and not messed with // This is in private route (reverse) order as we are receiving over the route - for (hop_n, hop_node_id) in rsd.hops.iter().rev().enumerate() { + for (hop_n, hop_node_ref) in rssd.hop_node_refs().iter().rev().enumerate() { // The last hop is not signed, as the whole packet is signed if hop_n == signatures.len() { // Verify the node we received the routed operation from is the last hop in our route - if hop_node_id != last_hop_id { - veilid_log!(self debug "received routed operation from the wrong hop ({} should be {}) on private route {}", hop_node_id, last_hop_id, public_key); + if !hop_node_ref.node_ids().contains(last_hop_id) { + veilid_log!(self debug "received routed operation from the wrong hop ({} should be {}) on private route {}", hop_node_ref, last_hop_id, public_key); return None; } } else { + let Some(hop_public_key) = hop_node_ref + .public_keys(RoutingDomain::PublicInternet) + .get(signatures[hop_n].kind()) + else { + veilid_log!(self debug "no hop public key matching signature kind {} at hop {} for routed operation on private route {}", signatures[hop_n].kind(), hop_n, public_key); + return None; + }; // Verify a signature for a hop node along the route - match vcrypto.verify( - &hop_node_id.ref_value().clone().into(), - data, - signatures[hop_n].ref_value(), - ) { + let Some(vcrypto) = crypto.get(hop_public_key.kind()) else { + veilid_log!(self debug "can't handle route hop with public key: {:?}", hop_public_key.kind()); + return None; + }; + match vcrypto.verify(&hop_public_key, data, &signatures[hop_n]) { Ok(true) => {} Ok(false) => { - veilid_log!(self debug "invalid signature for hop {} at {} on private route {}", hop_n, hop_node_id, public_key); + veilid_log!(self debug "invalid signature for hop {} at {} on private route {}", hop_n, hop_node_ref, public_key); return None; } Err(e) => { - veilid_log!(self debug "error verifying signature for hop {} at {} on private route {}: {}", hop_n, hop_node_id, public_key, e); + veilid_log!(self debug "error verifying signature for hop {} at {} on private route {}: {}", hop_n, hop_node_ref, public_key, e); return None; } } @@ -773,7 +775,7 @@ impl RouteSpecStore { }; // Get the hops so we can match the route's hop length for safety // route length as well as marking nodes as unreliable if this fails - let hops = rssd.hops_node_refs(); + let hops = rssd.hop_node_refs(); (key, hops) }; @@ -952,7 +954,10 @@ impl RouteSpecStore { && rssd.hop_count() >= min_hop_count && rssd.hop_count() <= max_hop_count && rssd.get_directions().is_superset(directions) - && rssd.get_route_set_keys().kinds().contains(&crypto_kind) + && rssd + .get_route_set_keys() + .iter() + .any(|x| x.kind() == crypto_kind) && !rssd.is_published() && !rssd.contains_nodes(avoid_nodes) { @@ -1072,7 +1077,7 @@ impl RouteSpecStore { let Some(vcrypto) = crypto.get(crypto_kind) else { apibail_generic!("crypto not supported for route"); }; - let pr_pubkey = private_route.public_key.value(); + let pr_pubkey = private_route.public_key.clone(); // See if we are using a safety route, if not, short circuit this operation let safety_spec = match safety_selection { @@ -1114,7 +1119,7 @@ impl RouteSpecStore { routing_table.public_key(crypto_kind), private_route, ), - secret: routing_table.secret_key(crypto_kind).value(), + secret: routing_table.secret_key(crypto_kind), first_hop, }); } @@ -1209,12 +1214,42 @@ impl RouteSpecStore { // Each loop mutates 'nonce', and 'blob_data' let mut nonce = vcrypto.random_nonce(); // Forward order (safety route), but inside-out - for h in (1..safety_rsd.hops.len()).rev() { + for h in (1..safety_rssd.hop_node_refs().len()).rev() { + let hop_node_ref = safety_rssd.hop_node_ref(h).unwrap(); + let Some(hop_node_id) = hop_node_ref.locked(rti).node_ids().get(crypto_kind) else { + apibail_invalid_argument!( + "no hop node id for route hop", + "crypto_kind", + crypto_kind + ); + }; + let Some(hop_public_key) = hop_node_ref + .locked(rti) + .public_keys(RoutingDomain::PublicInternet) + .get(crypto_kind) + else { + apibail_invalid_argument!( + "no hop public key for route hop", + "crypto_kind", + crypto_kind + ); + }; + let Some(hop_peer_info) = hop_node_ref + .locked(rti) + .get_peer_info(RoutingDomain::PublicInternet) + else { + apibail_invalid_argument!( + "no hop peer info for route hop", + "crypto_kind", + crypto_kind + ); + }; + // Get blob to encrypt for next hop blob_data = { // Encrypt the previous blob ENC(nonce, DH(PKhop,SKsr)) let dh_secret = vcrypto - .cached_dh(&safety_rsd.hops[h].value().into(), &safety_rsd.secret_key) + .cached_dh(&hop_public_key, &safety_rsd.secret_key) .map_err(VeilidAPIError::internal)?; let enc_msg_data = vcrypto .encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) @@ -1230,21 +1265,10 @@ impl RouteSpecStore { let route_hop = RouteHop { node: if optimize { // Optimized, no peer info, just the dht key - RouteNode::NodeId(safety_rsd.hops[h].clone()) + RouteNode::NodeId(hop_node_id) } else { // Full peer info, required until we are sure the route has been fully established - let node_id = safety_rsd.hops[h].clone(); - let pi = rti - .with_node_entry(node_id, |entry| { - entry.with(rti, |_rti, e| { - e.get_peer_info(RoutingDomain::PublicInternet) - }) - }) - .flatten(); - if pi.is_none() { - apibail_internal!("peer info should exist for route but doesn't"); - } - RouteNode::PeerInfo(pi.unwrap()) + RouteNode::PeerInfo(hop_peer_info) }, next_hop: Some(route_hop_data), }; @@ -1265,8 +1289,21 @@ impl RouteSpecStore { } // Encode first RouteHopData + let hop_node_ref = safety_rssd.hop_node_ref(0).unwrap(); + let Some(hop_public_key) = hop_node_ref + .locked(rti) + .public_keys(RoutingDomain::PublicInternet) + .get(crypto_kind) + else { + apibail_invalid_argument!( + "no hop public key for route hop", + "crypto_kind", + crypto_kind + ); + }; + let dh_secret = vcrypto - .cached_dh(&safety_rsd.hops[0].value().into(), &safety_rsd.secret_key) + .cached_dh(&hop_public_key, &safety_rsd.secret_key) .map_err(VeilidAPIError::internal)?; let enc_msg_data = vcrypto .encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) @@ -1282,7 +1319,7 @@ impl RouteSpecStore { // Build safety route let safety_route = SafetyRoute { - public_key: PublicKey::new(crypto_kind, sr_pubkey.clone()), + public_key: sr_pubkey, hops, }; @@ -1315,7 +1352,7 @@ impl RouteSpecStore { safety_spec: &SafetySpec, direction: DirectionSet, avoid_nodes: &[NodeId], - ) -> VeilidAPIResult { + ) -> VeilidAPIResult { // Ensure the total hop count isn't too long for our config let max_route_hop_count = self.max_route_hop_count; if safety_spec.hop_count == 0 { @@ -1340,7 +1377,7 @@ impl RouteSpecStore { if let Some(preferred_key) = preferred_rssd.get_route_set_keys().get(crypto_kind) { // Only use the preferred route if it doesn't contain the avoid nodes if !preferred_rssd.contains_nodes(avoid_nodes) { - return Ok(preferred_key.value()); + return Ok(preferred_key); } } } @@ -1378,8 +1415,7 @@ impl RouteSpecStore { .unwrap() .get_route_set_keys() .get(crypto_kind) - .unwrap() - .value(); + .unwrap(); Ok(sr_pubkey) } @@ -1391,7 +1427,7 @@ impl RouteSpecStore { crypto_kind: CryptoKind, safety_spec: &SafetySpec, avoid_nodes: &[NodeId], - ) -> VeilidAPIResult { + ) -> VeilidAPIResult { let inner = &mut *self.inner.lock(); let routing_table = self.routing_table(); let rti = &mut *routing_table.inner.write(); @@ -1408,8 +1444,9 @@ impl RouteSpecStore { fn assemble_private_route_inner( &self, - key: &BarePublicKey, + key: &PublicKey, rsd: &RouteSpecDetail, + rssd: &RouteSetSpecDetail, optimized: bool, ) -> VeilidAPIResult { let routing_table = self.routing_table(); @@ -1417,12 +1454,9 @@ impl RouteSpecStore { // Ensure we get the crypto for it let crypto = routing_table.network_manager().crypto(); - let Some(vcrypto) = crypto.get(rsd.crypto_kind) else { - apibail_invalid_argument!( - "crypto not supported for route", - "rsd.crypto_kind", - rsd.crypto_kind - ); + let crypto_kind = key.kind(); + let Some(vcrypto) = crypto.get(crypto_kind) else { + apibail_invalid_argument!("crypto not supported for route", "crypto_kind", crypto_kind); }; // Ensure our network class is valid before attempting to assemble any routes @@ -1434,11 +1468,11 @@ impl RouteSpecStore { // Make innermost route hop to our own node let mut route_hop = RouteHop { node: if optimized { - let Some(node_id) = routing_table.node_ids().get(rsd.crypto_kind) else { + let Some(node_id) = routing_table.node_ids().get(crypto_kind) else { apibail_invalid_argument!( "missing node id for crypto kind", - "rsd.crypto_kind", - rsd.crypto_kind + "crypto_kind", + crypto_kind ); }; RouteNode::NodeId(node_id) @@ -1448,10 +1482,37 @@ impl RouteSpecStore { next_hop: None, }; - // Loop for each hop - let hop_count = rsd.hops.len(); - // iterate hops in private route order (reverse, but inside out) - for h in 0..hop_count { + // Iterate hops in private route order (reverse, but inside out) + for hop_node_ref in rssd.hop_node_refs() { + let hop_node_ref = hop_node_ref.locked(rti); + + let Some(hop_node_id) = hop_node_ref.node_ids().get(crypto_kind) else { + apibail_invalid_argument!( + "no hop node id for route hop", + "crypto_kind", + crypto_kind + ); + }; + let Some(hop_public_key) = hop_node_ref + .public_keys(RoutingDomain::PublicInternet) + .get(crypto_kind) + else { + apibail_invalid_argument!( + "no hop public key for route hop", + "crypto_kind", + crypto_kind + ); + }; + let Some(hop_peer_info) = hop_node_ref.get_peer_info(RoutingDomain::PublicInternet) + else { + apibail_invalid_argument!( + "no hop peer info for route hop", + "crypto_kind", + crypto_kind + ); + }; + + // Encrypt the previous blob ENC(nonce, DH(PKhop,SKpr)) let nonce = vcrypto.random_nonce(); let blob_data = { @@ -1461,8 +1522,7 @@ impl RouteSpecStore { message_builder_to_vec(rh_message)? }; - // Encrypt the previous blob ENC(nonce, DH(PKhop,SKpr)) - let dh_secret = vcrypto.cached_dh(&rsd.hops[h].value().into(), &rsd.secret_key)?; + let dh_secret = vcrypto.cached_dh(&hop_public_key, &rsd.secret_key)?; let enc_msg_data = vcrypto.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)?; let route_hop_data = RouteHopData { @@ -1473,28 +1533,17 @@ impl RouteSpecStore { route_hop = RouteHop { node: if optimized { // Optimized, no peer info, just the dht key - RouteNode::NodeId(rsd.hops[h].clone()) + RouteNode::NodeId(hop_node_id) } else { // Full peer info, required until we are sure the route has been fully established - let node_id = rsd.hops[h].clone(); - let pi = rti - .with_node_entry(node_id, |entry| { - entry.with(rti, |_rti, e| { - e.get_peer_info(RoutingDomain::PublicInternet) - }) - }) - .flatten(); - if pi.is_none() { - apibail_internal!("peer info should exist for route but doesn't"); - } - RouteNode::PeerInfo(pi.unwrap()) + RouteNode::PeerInfo(hop_peer_info) }, next_hop: Some(route_hop_data), } } let private_route = PrivateRoute { - public_key: PublicKey::new(rsd.crypto_kind, key.clone()), + public_key: key.clone(), hops: PrivateRouteHops::FirstHop(Box::new(route_hop)), }; Ok(private_route) @@ -1505,7 +1554,7 @@ impl RouteSpecStore { #[instrument(level = "trace", target = "route", skip_all)] pub fn assemble_private_route( &self, - key: &BarePublicKey, + key: &PublicKey, optimized: Option, ) -> VeilidAPIResult { let inner: &RouteSpecStoreInner = &self.inner.lock(); @@ -1525,7 +1574,7 @@ impl RouteSpecStore { .get_route_by_key(key) .expect("route key index is broken"); - self.assemble_private_route_inner(key, rsd, optimized) + self.assemble_private_route_inner(key, rsd, rssd, optimized) } /// Assemble private route set for publication @@ -1547,7 +1596,7 @@ impl RouteSpecStore { let mut out = Vec::new(); for (key, rsd) in rssd.iter_route_set() { - out.push(self.assemble_private_route_inner(key, rsd, optimized)?); + out.push(self.assemble_private_route_inner(key, rsd, rssd, optimized)?); } Ok(out) } @@ -1631,7 +1680,7 @@ impl RouteSpecStore { } /// Get a route id for a route's public key - pub fn get_route_id_for_key(&self, key: &BarePublicKey) -> Option { + pub fn get_route_id_for_key(&self, key: &PublicKey) -> Option { let inner = &mut *self.inner.lock(); // Check for local route if let Some(id) = inner.content.get_id_by_key(key) { @@ -1650,7 +1699,7 @@ impl RouteSpecStore { /// This happens when you communicate with a private route without a safety route pub fn has_remote_private_route_seen_our_node_info( &self, - key: &BarePublicKey, + key: &PublicKey, published_peer_info: &PeerInfo, ) -> bool { let inner = &*self.inner.lock(); @@ -1680,7 +1729,7 @@ impl RouteSpecStore { /// was that node that had the private route. pub fn mark_remote_private_route_seen_our_node_info( &self, - key: &BarePublicKey, + key: &PublicKey, cur_ts: Timestamp, ) -> VeilidAPIResult<()> { let Some(our_node_info_ts) = self @@ -1711,22 +1760,14 @@ impl RouteSpecStore { } /// Get the route statistics for any route we know about, local or remote - pub fn with_route_stats_mut( - &self, - cur_ts: Timestamp, - key: &BarePublicKey, - f: F, - ) -> Option + pub fn with_route_stats_mut(&self, cur_ts: Timestamp, key: &PublicKey, f: F) -> Option where F: FnOnce(&mut RouteStats) -> R, { let inner = &mut *self.inner.lock(); // Check for stub route - if self - .routing_table() - .matches_own_node_id_key(&key.clone().into()) - { + if self.routing_table().public_keys().contains(key) { return None; } @@ -1893,7 +1934,7 @@ impl RouteSpecStore { Ok(RouteId::new( vcrypto.kind(), - BareRouteId::new(vcrypto.generate_hash(&pkbytes).bytes()), + BareRouteId::new(vcrypto.generate_hash(&pkbytes).ref_value()), )) } @@ -1927,7 +1968,7 @@ impl RouteSpecStore { Ok(RouteId::new( vcrypto.kind(), - BareRouteId::new(vcrypto.generate_hash(&pkbytes).bytes()), + BareRouteId::new(vcrypto.generate_hash(&pkbytes).ref_value()), )) } } diff --git a/veilid-core/src/routing_table/route_spec_store/permutation.rs b/veilid-core/src/routing_table/route_spec_store/permutation.rs index fd832b92..97140953 100644 --- a/veilid-core/src/routing_table/route_spec_store/permutation.rs +++ b/veilid-core/src/routing_table/route_spec_store/permutation.rs @@ -15,7 +15,7 @@ fn _get_route_permutation_count(hop_count: usize) -> usize { // hop_count = 4 -> 3! -> 6 (3..hop_count).fold(2usize, |acc, x| acc * x) } -pub type PermReturnType = (Vec, bool); +pub type PermReturnType = (Vec, SequenceOrderingSet); pub type PermFunc<'t> = Box Option + Send + 't>; /// get the route permutation at particular 'perm' index, starting at the 'start' index diff --git a/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs b/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs index a738f786..0380cf7c 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs @@ -2,18 +2,16 @@ use super::*; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RouteSpecDetail { - /// Crypto kind - pub crypto_kind: CryptoKind, /// Secret key - pub secret_key: BareSecretKey, - /// Route hops (node id keys) + pub secret_key: SecretKey, + /// Route hop node ids pub hops: Vec, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RouteSetSpecDetail { - /// Route set per crypto kind - route_set: BTreeMap, + /// Routes in the set + route_set: BTreeMap, /// Route noderefs #[serde(skip)] hop_node_refs: Vec, @@ -26,7 +24,7 @@ pub struct RouteSetSpecDetail { /// Stability preference (prefer reliable nodes over faster) stability: Stability, /// Sequencing capability (connection oriented protocols vs datagram) - can_do_sequenced: bool, + orderings: SequenceOrderingSet, /// Stats stats: RouteStats, /// Automatically allocated route vs manually allocated route @@ -36,11 +34,11 @@ pub struct RouteSetSpecDetail { impl RouteSetSpecDetail { pub fn new( cur_ts: Timestamp, - route_set: BTreeMap, + route_set: BTreeMap, hop_node_refs: Vec, directions: DirectionSet, stability: Stability, - can_do_sequenced: bool, + orderings: SequenceOrderingSet, automatic: bool, ) -> Self { Self { @@ -49,32 +47,50 @@ impl RouteSetSpecDetail { published: false, directions, stability, - can_do_sequenced, + orderings, stats: RouteStats::new(cur_ts), automatic, } } - pub fn get_route_by_key(&self, key: &BarePublicKey) -> Option<&RouteSpecDetail> { + #[expect(dead_code)] + pub fn len(&self) -> usize { + self.route_set.len() + } + #[expect(dead_code)] + pub fn is_empty(&self) -> bool { + self.route_set.is_empty() + } + pub fn get_route_by_key(&self, key: &PublicKey) -> Option<&RouteSpecDetail> { self.route_set.get(key) } pub fn get_route_set_keys(&self) -> PublicKeyGroup { let mut tks = PublicKeyGroup::new(); - for (k, v) in &self.route_set { - tks.add(PublicKey::new(v.crypto_kind, k.clone())); + for k in self.route_set.keys() { + tks.add(k.clone()); } tks } - pub fn get_best_route_set_key(&self) -> Option { - self.get_route_set_keys().best().map(|k| k.value()) + pub fn get_best_route_set_key(&self) -> Option { + self.get_route_set_keys().first().cloned() } pub fn set_hop_node_refs(&mut self, node_refs: Vec) { self.hop_node_refs = node_refs; } pub fn iter_route_set( &self, - ) -> alloc::collections::btree_map::Iter<'_, BarePublicKey, RouteSpecDetail> { + ) -> alloc::collections::btree_map::Iter<'_, PublicKey, RouteSpecDetail> { self.route_set.iter() } + #[expect(dead_code)] + pub fn iter_route_set_mut( + &mut self, + ) -> alloc::collections::btree_map::IterMut<'_, PublicKey, RouteSpecDetail> { + self.route_set.iter_mut() + } + #[expect(dead_code)] + pub fn remove_route(&mut self, key: &PublicKey) { + self.route_set.remove(key); + } pub fn get_stats(&self) -> &RouteStats { &self.stats } @@ -90,7 +106,7 @@ impl RouteSetSpecDetail { pub fn hop_count(&self) -> usize { self.hop_node_refs.len() } - pub fn hops_node_refs(&self) -> Vec { + pub fn hop_node_refs(&self) -> Vec { self.hop_node_refs.clone() } pub fn hop_node_ref(&self, idx: usize) -> Option { @@ -103,16 +119,17 @@ impl RouteSetSpecDetail { self.directions } pub fn is_sequencing_match(&self, sequencing: Sequencing) -> bool { - match sequencing { - Sequencing::NoPreference => true, - Sequencing::PreferOrdered => true, - Sequencing::EnsureOrdered => self.can_do_sequenced, + for ordering in self.orderings.iter() { + if sequencing.matches_ordering(ordering) { + return true; + } } + false } pub fn contains_nodes(&self, nodes: &[NodeId]) -> bool { for tk in nodes { for rsd in self.route_set.values() { - if rsd.crypto_kind == tk.kind() && rsd.hops.contains(tk) { + if rsd.hops.contains(tk) { return true; } } @@ -122,23 +139,15 @@ impl RouteSetSpecDetail { pub fn is_automatic(&self) -> bool { self.automatic } - /// Generate a key for the cache that can be used to uniquely identify this route's contents - pub fn make_cache_key(&self, rti: &RoutingTableInner) -> Option> { + pub fn make_cache_key(&self, rti: &RoutingTableInner) -> Vec { let hops = &self.hop_node_refs; - - let mut cachelen = 0usize; - let mut nodebytes = Vec::::with_capacity(hops.len()); + let mut cache: Vec = Vec::with_capacity(hops.len() * 32); // xxx hack: this code is going away soon anyway for hop in hops { - let b = hop.locked(rti).best_node_id()?.value(); - cachelen += b.len(); - nodebytes.push(b); + if let Some(b) = hop.locked(rti).best_node_id() { + cache.extend_from_slice(b.ref_value()); + } } - let mut cache: Vec = Vec::with_capacity(cachelen); - for b in nodebytes { - cache.extend_from_slice(&b); - } - - Some(cache) + cache } } diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs index 98c46b40..a78a0096 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs @@ -4,8 +4,8 @@ impl_veilid_log_facility!("rtab"); // Compiled route key for caching #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] struct CompiledRouteCacheKey { - sr_pubkey: BarePublicKey, - pr_pubkey: BarePublicKey, + sr_pubkey: PublicKey, + pr_pubkey: PublicKey, } /// Compiled route (safety route + private route) @@ -14,7 +14,7 @@ pub struct CompiledRoute { /// The safety route attached to the private route pub safety_route: SafetyRoute, /// The secret used to encrypt the message payload - pub secret: BareSecretKey, + pub secret: SecretKey, /// The node ref to the first hop in the compiled route /// filtered to the safetyselection it was compiled with pub first_hop: FilteredNodeRef, @@ -34,7 +34,7 @@ pub struct RouteSpecStoreCache { /// Remote private routes we've imported and statistics remote_private_route_set_cache: LruCache, /// Remote private route ids indexed by route's public key - remote_private_routes_by_key: HashMap, + remote_private_routes_by_key: HashMap, /// Compiled route cache compiled_route_cache: LruCache, /// List of dead allocated routes @@ -62,9 +62,7 @@ impl RouteSpecStoreCache { /// add an allocated route set to our cache via its cache key pub fn add_to_cache(&mut self, rti: &RoutingTableInner, rssd: &RouteSetSpecDetail) { - let Some(cache_key) = rssd.make_cache_key(rti) else { - panic!("all routes should have a cache key"); - }; + let cache_key = rssd.make_cache_key(rti); if !self.hop_cache.insert(cache_key) { panic!("route should never be inserted twice"); } @@ -94,9 +92,7 @@ impl RouteSpecStoreCache { id: RouteId, rssd: &RouteSetSpecDetail, ) -> bool { - let Some(cache_key) = rssd.make_cache_key(rti) else { - panic!("all routes should have a cache key"); - }; + let cache_key = rssd.make_cache_key(rti); // Remove from hop cache if !self.hop_cache.remove(&cache_key) { @@ -161,7 +157,7 @@ impl RouteSpecStoreCache { // also store in id by key table for private_route in rprinfo.get_private_routes() { self.remote_private_routes_by_key - .insert(private_route.public_key.value(), id.clone()); + .insert(private_route.public_key.clone(), id.clone()); } let mut dead = None; @@ -179,9 +175,9 @@ impl RouteSpecStoreCache { for dead_private_route in dead_rpri.get_private_routes() { let _ = self .remote_private_routes_by_key - .remove(dead_private_route.public_key.ref_value()) + .remove(&dead_private_route.public_key) .unwrap(); - self.invalidate_compiled_route_cache(dead_private_route.public_key.ref_value()); + self.invalidate_compiled_route_cache(&dead_private_route.public_key); } self.dead_remote_routes.push(dead_id); } @@ -265,7 +261,7 @@ impl RouteSpecStoreCache { } /// look up a remote private route id by one of the route public keys - pub fn get_remote_private_route_id_by_key(&self, key: &BarePublicKey) -> Option { + pub fn get_remote_private_route_id_by_key(&self, key: &PublicKey) -> Option { self.remote_private_routes_by_key.get(key).cloned() } @@ -307,22 +303,18 @@ impl RouteSpecStoreCache { for private_route in rprinfo.get_private_routes() { let _ = self .remote_private_routes_by_key - .remove(private_route.public_key.ref_value()) + .remove(&private_route.public_key) .unwrap(); - self.invalidate_compiled_route_cache(private_route.public_key.ref_value()); + self.invalidate_compiled_route_cache(&private_route.public_key); } self.dead_remote_routes.push(id); true } /// Stores a compiled 'safety + private' route so we don't have to compile it again later - pub fn add_to_compiled_route_cache( - &mut self, - pr_pubkey: BarePublicKey, - safety_route: SafetyRoute, - ) { + pub fn add_to_compiled_route_cache(&mut self, pr_pubkey: PublicKey, safety_route: SafetyRoute) { let key = CompiledRouteCacheKey { - sr_pubkey: safety_route.public_key.value(), + sr_pubkey: safety_route.public_key.clone(), pr_pubkey: pr_pubkey.clone(), }; @@ -334,8 +326,8 @@ impl RouteSpecStoreCache { /// Looks up an existing compiled route from the safety and private route components pub fn lookup_compiled_route_cache( &mut self, - sr_pubkey: BarePublicKey, - pr_pubkey: BarePublicKey, + sr_pubkey: PublicKey, + pr_pubkey: PublicKey, ) -> Option { let key = CompiledRouteCacheKey { sr_pubkey, @@ -345,7 +337,7 @@ impl RouteSpecStoreCache { } /// When routes are dropped, they should be removed from the compiled route cache - fn invalidate_compiled_route_cache(&mut self, dead_key: &BarePublicKey) { + fn invalidate_compiled_route_cache(&mut self, dead_key: &PublicKey) { let mut dead_entries = Vec::new(); for (k, _v) in self.compiled_route_cache.iter() { if k.sr_pubkey == *dead_key || k.pr_pubkey == *dead_key { diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store_content.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store_content.rs index 023cd427..08948bdd 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store_content.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store_content.rs @@ -4,7 +4,7 @@ use super::*; #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub(super) struct RouteSpecStoreContent { /// All of the route sets we have allocated so far indexed by key (many to one) - id_by_key: HashMap, + id_by_key: HashMap, /// All of the route sets we have allocated so far details: HashMap, } @@ -44,8 +44,9 @@ impl RouteSpecStoreContent { // Apply noderefs rssd.set_hop_node_refs(hop_node_refs); } + for id in dead_ids { - veilid_log!(table_store trace "no entry, killing off private route: {}", id); + veilid_log!(table_store trace "no entry, killing route set: {}", id); content.remove_detail(&id); } @@ -86,7 +87,7 @@ impl RouteSpecStoreContent { pub fn get_detail_mut(&mut self, id: &RouteId) -> Option<&mut RouteSetSpecDetail> { self.details.get_mut(id) } - pub fn get_id_by_key(&self, key: &BarePublicKey) -> Option { + pub fn get_id_by_key(&self, key: &PublicKey) -> Option { self.id_by_key.get(key).cloned() } // pub fn iter_ids(&self) -> std::collections::hash_map::Keys { diff --git a/veilid-core/src/routing_table/routing_table_inner/mod.rs b/veilid-core/src/routing_table/routing_table_inner/mod.rs index 3af0fb51..1d6d3306 100644 --- a/veilid-core/src/routing_table/routing_table_inner/mod.rs +++ b/veilid-core/src/routing_table/routing_table_inner/mod.rs @@ -840,6 +840,7 @@ impl RoutingTableInner { } /// Resolve an existing routing table entry and call a function on its entry without using a noderef + #[expect(dead_code)] pub fn with_node_entry(&self, node_id: NodeId, f: F) -> Option where F: FnOnce(Arc) -> R, @@ -886,7 +887,7 @@ impl RoutingTableInner { let node_info = peer_info.node_info(); let relay_ids = node_info.relay_ids(); let node_ids = peer_info.node_ids().clone(); - if node_ids.contains_any(&relay_ids) { + if node_ids.contains_any_from_slice(&relay_ids) { bail!("node can not be its own relay"); } @@ -972,7 +973,7 @@ impl RoutingTableInner { "routing domains should be the same here", ); let mut node_ids = old_pi.node_ids().clone(); - node_ids.add_all(new_pi.node_ids()); + node_ids.add_all_from_slice(new_pi.node_ids()); (new_pi.routing_domain(), node_ids) } }; @@ -986,7 +987,7 @@ impl RoutingTableInner { .iter() .flat_map(|rdr| rdr.relay_node.locked(rti).node_ids().to_vec()) .collect::>(); - if node_ids.contains_any(&our_relay_node_ids) { + if node_ids.contains_any_from_slice(&our_relay_node_ids) { rd.refresh(); rd.publish_peer_info(rti); } @@ -1102,6 +1103,7 @@ impl RoutingTableInner { } #[instrument(level = "trace", skip_all)] + #[expect(dead_code)] pub fn transform_to_peer_info( &self, routing_domain: RoutingDomain, @@ -1260,7 +1262,7 @@ impl RoutingTableInner { pub fn find_preferred_closest_nodes( &self, node_count: usize, - hash_coordinate: HashDigest, + hash_coordinate: HashCoordinate, mut filters: VecDeque, transform: T, ) -> VeilidAPIResult> @@ -1272,10 +1274,6 @@ impl RoutingTableInner { // Get the crypto kind let crypto_kind = hash_coordinate.kind(); - let crypto = self.crypto(); - let Some(vcrypto) = crypto.get(crypto_kind) else { - apibail_generic!("invalid crypto kind"); - }; // Filter to ensure entries support the crypto kind in use // always filter out dead and punished nodes @@ -1334,14 +1332,14 @@ impl RoutingTableInner { }; // distance is the next metric, closer nodes first - let da = vcrypto.distance( - &BareHashDigest::from(a_key.value()), - hash_coordinate.ref_value(), - ); - let db = vcrypto.distance( - &BareHashDigest::from(b_key.value()), - hash_coordinate.ref_value(), - ); + let da = a_key + .ref_value() + .to_bare_hash_coordinate() + .distance(hash_coordinate.ref_value()); + let db = b_key + .ref_value() + .to_bare_hash_coordinate() + .distance(hash_coordinate.ref_value()); da.cmp(&db) }; @@ -1354,11 +1352,11 @@ impl RoutingTableInner { #[instrument(level = "trace", skip_all)] pub fn sort_and_clean_closest_noderefs( &self, - node_id: HashDigest, + hash_coordinate: HashCoordinate, closest_nodes: &[NodeRef], ) -> Vec { // Lock all noderefs - let kind = node_id.kind(); + let kind = hash_coordinate.kind(); let mut closest_nodes_locked: Vec = closest_nodes .iter() .filter_map(|nr| { @@ -1372,8 +1370,7 @@ impl RoutingTableInner { .collect(); // Sort closest - let crypto = self.crypto(); - let sort = make_closest_noderef_sort(&crypto, node_id); + let sort = make_closest_noderef_sort(hash_coordinate); closest_nodes_locked.sort_by(sort); // Unlock noderefs @@ -1530,14 +1527,10 @@ impl RoutingTableInner { } } -#[instrument(level = "trace", skip_all)] -pub fn make_closest_noderef_sort<'a>( - crypto: &'a Crypto, - hash_coordinate: HashDigest, -) -> impl Fn(&LockedNodeRef, &LockedNodeRef) -> core::cmp::Ordering + 'a { +pub fn make_closest_noderef_sort( + hash_coordinate: HashCoordinate, +) -> impl Fn(&LockedNodeRef, &LockedNodeRef) -> core::cmp::Ordering { let kind = hash_coordinate.kind(); - // Get cryptoversion to check distance with - let vcrypto = crypto.get(kind).unwrap(); move |a: &LockedNodeRef, b: &LockedNodeRef| -> core::cmp::Ordering { // same nodes are always the same @@ -1552,38 +1545,42 @@ pub fn make_closest_noderef_sort<'a>( let b_key = b_entry.node_ids().get(kind).unwrap(); // distance is the next metric, closer nodes first - let da = vcrypto.distance( - &BareHashDigest::from(a_key.value()), - hash_coordinate.ref_value(), - ); - let db = vcrypto.distance( - &BareHashDigest::from(b_key.value()), - hash_coordinate.ref_value(), - ); + let da = a_key + .ref_value() + .to_bare_hash_coordinate() + .distance(hash_coordinate.ref_value()); + let db = b_key + .ref_value() + .to_bare_hash_coordinate() + .distance(hash_coordinate.ref_value()); da.cmp(&db) }) }) } } -pub fn make_closest_node_id_sort( - crypto: &Crypto, - hash_coordinate: HashDigest, -) -> impl Fn(&BareNodeId, &BareNodeId) -> core::cmp::Ordering + '_ { - let kind = hash_coordinate.kind(); - // Get cryptoversion to check distance with - let vcrypto = crypto.get(kind).unwrap(); - +pub fn make_closest_bare_node_id_sort( + bare_hash_coordinate: BareHashCoordinate, +) -> impl Fn(&BareNodeId, &BareNodeId) -> core::cmp::Ordering { move |a: &BareNodeId, b: &BareNodeId| -> core::cmp::Ordering { - // distance is the next metric, closer nodes first - let da = vcrypto.distance( - &BareHashDigest::from(a.bytes()), - hash_coordinate.ref_value(), - ); - let db = vcrypto.distance( - &BareHashDigest::from(b.bytes()), - hash_coordinate.ref_value(), - ); + let da = a.to_bare_hash_coordinate().distance(&bare_hash_coordinate); + let db = b.to_bare_hash_coordinate().distance(&bare_hash_coordinate); + da.cmp(&db) + } +} + +pub fn make_closest_node_id_sort( + hash_coordinate: HashCoordinate, +) -> impl Fn(&NodeId, &NodeId) -> core::cmp::Ordering { + move |a: &NodeId, b: &NodeId| -> core::cmp::Ordering { + let da = a + .ref_value() + .to_bare_hash_coordinate() + .distance(hash_coordinate.ref_value()); + let db = b + .ref_value() + .to_bare_hash_coordinate() + .distance(hash_coordinate.ref_value()); da.cmp(&db) } } diff --git a/veilid-core/src/routing_table/routing_table_inner/routing_domains/mod.rs b/veilid-core/src/routing_table/routing_table_inner/routing_domains/mod.rs index 65dc95af..37082e21 100644 --- a/veilid-core/src/routing_table/routing_table_inner/routing_domains/mod.rs +++ b/veilid-core/src/routing_table/routing_table_inner/routing_domains/mod.rs @@ -58,7 +58,6 @@ pub trait RoutingDomainDetail { ) -> ContactMethod; // Bootstrap peers - #[expect(dead_code)] fn get_bootstrap_peers(&self) -> Vec; fn clear_bootstrap_peers(&self); fn add_bootstrap_peer(&self, bootstrap_peer: NodeRef); diff --git a/veilid-core/src/routing_table/routing_table_inner/routing_domains/public_internet/mod.rs b/veilid-core/src/routing_table/routing_table_inner/routing_domains/public_internet/mod.rs index df8a7c1d..8dd425d1 100644 --- a/veilid-core/src/routing_table/routing_table_inner/routing_domains/public_internet/mod.rs +++ b/veilid-core/src/routing_table/routing_table_inner/routing_domains/public_internet/mod.rs @@ -69,7 +69,10 @@ impl PublicInternetRoutingDomainDetail { // Note that relay_peer_info could be node_a, in which case a connection already exists // and we only get here if the connection had dropped, in which case node_a is unreachable until // it gets a new relay connection up - if node_b_relay.node_ids().contains_any(ctx.peer_a.node_ids()) { + if node_b_relay + .node_ids() + .contains_any_from_slice(ctx.peer_a.node_ids()) + { return Some(ContactMethod::Existing); } @@ -163,7 +166,10 @@ impl PublicInternetRoutingDomainDetail { // Note that relay_peer_info could be node_a, in which case a connection already exists // and we only get here if the connection had dropped, in which case node_b is unreachable until // it gets a new relay connection up - if node_b_relay.node_ids().contains_any(ctx.peer_a.node_ids()) { + if node_b_relay + .node_ids() + .contains_any_from_slice(ctx.peer_a.node_ids()) + { return Some(ContactMethod::Existing); } diff --git a/veilid-core/src/routing_table/routing_table_inner/routing_domains/relay_status.rs b/veilid-core/src/routing_table/routing_table_inner/routing_domains/relay_status.rs index 632eeeb0..892be5d8 100644 --- a/veilid-core/src/routing_table/routing_table_inner/routing_domains/relay_status.rs +++ b/veilid-core/src/routing_table/routing_table_inner/routing_domains/relay_status.rs @@ -27,7 +27,7 @@ pub struct RelayStatus { /// All protocol/address types requiring inbound relays for this node pub need_relay_protocols: HashSet<(ProtocolType, AddressType)>, /// Ordering modes we still need for relaying, per address type - pub need_relay_orderings: HashSet<(bool, AddressType)>, + pub need_relay_orderings: HashSet<(SequenceOrdering, AddressType)>, /// All protocol/address types we can offer inbound relaying for from this node #[expect(dead_code)] pub can_relay_protocols: HashSet<(ProtocolType, AddressType)>, @@ -71,7 +71,7 @@ impl RelayStatus { let ordering_modes = node_info .outbound_protocols() .iter() - .map(|x| x.is_ordered()) + .map(|x| x.sequence_ordering()) .collect::>(); // Get the dial info list in preferred deterministic order @@ -108,7 +108,7 @@ impl RelayStatus { for at in AddressTypeSet::all() { for pt in ProtocolTypeSet::all() { // if we can't use this protocol because we don't have its ordering mode enabled at all, then we should exclude it - if !ordering_modes.contains(&pt.is_ordered()) { + if !ordering_modes.contains(&pt.sequence_ordering()) { continue; } @@ -117,7 +117,7 @@ impl RelayStatus { can_relay_protocols.insert((pt, at)); // Note the relay ordering - need_relay_orderings.remove(&(pt.is_ordered(), at)); + need_relay_orderings.remove(&(pt.sequence_ordering(), at)); } if needs_hairpin_nat_support || !direct_did_protoaddrs.contains(&(pt, at)) { @@ -228,7 +228,7 @@ impl RelayStatus { // Determine for this relay, if there are dialinfo that are reachable with our node's // dialinfo filter, and which ordering modes can be satisfied by those flows - let mut possible_ordering_modes = HashSet::::new(); + let mut possible_ordering_modes = SequenceOrderingSet::new(); for did in &dial_info_list { if did.class.requires_signal() { continue; @@ -238,7 +238,7 @@ impl RelayStatus { // If this dial info can be contacted directly, then it can be used for receiving // relaying and satsifying an ordering mode if did.dial_info.matches_filter(&self.dial_info_filter) { - possible_ordering_modes.insert(didpa.0.is_ordered()); + possible_ordering_modes.insert(didpa.0.sequence_ordering()); } } @@ -267,7 +267,7 @@ impl RelayStatus { // Mark this ordering mode as satisfied self.need_relay_orderings - .remove(&(didpa.0.is_ordered(), didpa.1)); + .remove(&(didpa.0.sequence_ordering(), didpa.1)); } } @@ -284,8 +284,8 @@ impl RelayStatus { let mut add_ping = false; // See if we should add the ping for ordering mode coverage - let is_ordered = didpa.0.is_ordered(); - add_ping |= possible_ordering_modes.remove(&is_ordered); + let ordering = didpa.0.sequence_ordering(); + add_ping |= possible_ordering_modes.remove(ordering); // See if we should add the ping for low level port mapping coverage if let Some((llpt, port)) = self diff --git a/veilid-core/src/routing_table/routing_table_inner/routing_domains/relay_status.rs.orig b/veilid-core/src/routing_table/routing_table_inner/routing_domains/relay_status.rs.orig deleted file mode 100644 index e9bb807a..00000000 --- a/veilid-core/src/routing_table/routing_table_inner/routing_domains/relay_status.rs.orig +++ /dev/null @@ -1,318 +0,0 @@ -use super::*; - -#[derive(Debug, Clone)] -pub struct RelayPing { - pub node_ref: FilteredNodeRef, -} - -impl PartialEq for RelayPing { - fn eq(&self, other: &Self) -> bool { - self.node_ref.equivalent(&other.node_ref) - } -} - -impl Eq for RelayPing {} - -/// The current node's relaying capabilities and requirements -#[derive(Debug, Clone)] -pub struct RelayStatus { - /// Routing domain this is for - pub routing_domain: RoutingDomain, - /// Low level port info for this node - /// This is which ports are mapped externally that we may need keepalive pings for - pub low_level_port_info: LowLevelPortInfo, - /// This node's outbound dial info filter - /// Used to determine if a relay's dialinfo is directly reachable - pub dial_info_filter: DialInfoFilter, - /// All protocol/address types requiring inbound relays for this node - pub need_relay_protocols: HashSet<(ProtocolType, AddressType)>, - /// Ordering modes we still need for relaying, per address type - pub need_relay_orderings: HashSet<(bool, AddressType)>, - /// All protocol/address types we can offer inbound relaying for from this node - #[expect(dead_code)] - pub can_relay_protocols: HashSet<(ProtocolType, AddressType)>, - /// All the low level protocols and ports that require nat keepalive pings - pub wants_nat_keepalives: LowLevelProtocolPorts, - /// All of the relays and their configuration currently included in our status - pub relays: Vec, -} - -impl RelayStatus { - pub fn new_from_routing_domain_detail( - rdd: &dyn RoutingDomainDetail, - needs_hairpin_nat_support: bool, - ) -> Self { - // Make temporary nodeinfo without relay info or keys - let node_info = NodeInfo::new( - Timestamp::now(), - VALID_ENVELOPE_VERSIONS.to_vec(), - vec![], - rdd.capabilities(), - rdd.outbound_protocols(), - rdd.address_types(), - rdd.dial_info_details().clone(), - vec![], - ); - - Self::new_from_node_info(rdd.routing_domain(), &node_info, needs_hairpin_nat_support) - } - - fn new_from_node_info( - routing_domain: RoutingDomain, - node_info: &NodeInfo, - needs_hairpin_nat_support: bool, - ) -> Self { - let low_level_port_info = node_info.get_low_level_port_info(); - let dial_info_filter = DialInfoFilter::all() - .with_protocol_type_set(node_info.outbound_protocols()) - .with_address_type_set(node_info.address_types()); - - // Determine ordering modes we need relaying for - let ordering_modes = node_info - .outbound_protocols() - .iter() - .map(|x| x.is_ordered()) - .collect::>(); - - // Get the dial info list in preferred deterministic order - let mut dial_info_list = node_info.dial_info_detail_list().to_vec(); - dial_info_list.sort_by(DialInfoDetail::ordered_sequencing_sort); - - // Figure out which dial info combinations we have that are direct-capable - let mut direct_did_protoaddrs = HashSet::<(ProtocolType, AddressType)>::new(); - let mut wants_nat_keepalives = LowLevelProtocolPorts::new(); - for did in dial_info_list { - if !did.class.requires_signal() { - direct_did_protoaddrs - .insert((did.dial_info.protocol_type(), did.dial_info.address_type())); - } - if did.class.wants_nat_keepalive() { - wants_nat_keepalives.insert(( - did.dial_info.protocol_type().low_level_protocol_type(), - did.dial_info.address_type(), - did.dial_info.port(), - )); - } - } - - // Calculate which address/protocol type combinations we require - // relays for, and which we can offer relay support for - let mut need_relay_protocols = HashSet::<(ProtocolType, AddressType)>::new(); - let mut can_relay_protocols = HashSet::<(ProtocolType, AddressType)>::new(); - for at in AddressTypeSet::all() { - for pt in ProtocolTypeSet::all() { - // if we can't use this protocol because we don't have its ordering mode enabled at all, then we should exclude it - if !ordering_modes.contains(&pt.is_ordered()) { - continue; - } - - if direct_did_protoaddrs.contains(&(pt, at)) { - // We can relay this combination - can_relay_protocols.insert((pt, at)); - } - - if needs_hairpin_nat_support || !direct_did_protoaddrs.contains(&(pt, at)) { - // We can't relay this, so we must need a relay for it ourselves - // Or we want to allocate a hairpin NAT relay - need_relay_protocols.insert((pt, at)); - } - } - } - - // Get the ordering modes per address type we need, at a minimum, to be able to publish peer info - let need_relay_orderings = AddressTypeSet::all() - .iter() - .flat_map(|at| ordering_modes.iter().map(move |om| (*om, at))) - .collect::>(); - - RelayStatus { - routing_domain, - low_level_port_info, - dial_info_filter, - need_relay_protocols, - need_relay_orderings, - can_relay_protocols, - wants_nat_keepalives, - relays: vec![], - } - } - - /// Check if we would like more relays - pub fn wants_more_relays(&self) -> bool { - !self.need_relay_protocols.is_empty() || !self.wants_nat_keepalives.is_empty() - } - - /// Check if we need more relays before publication - pub fn needs_more_relays(&self) -> bool { - !self.need_relay_orderings.is_empty() || !self.wants_nat_keepalives.is_empty() - } - - /// Get the routing domain relays list when we're done - pub fn get_sorted_relays_list(&self) -> Vec { - let mut relays = self.relays.clone(); - - // Sort things in order of relay preference, using least-capable relays first - relays.sort_by(|ardr, brdr| { - // Get address types and protocol types to sort by - let mut aats = AddressTypeSet::new(); - let mut apts = ProtocolTypeSet::new(); - for did in &ardr.dial_info_details { - aats |= did.dial_info.address_type(); - apts |= did.dial_info.protocol_type(); - } - - let mut bats = AddressTypeSet::new(); - let mut bpts = ProtocolTypeSet::new(); - for did in &brdr.dial_info_details { - bats |= did.dial_info.address_type(); - bpts |= did.dial_info.protocol_type(); - } - - // Compare by address type set first (fewer address types is less) - let c = aats.len().cmp(&bats.len()); - if c != cmp::Ordering::Equal { - return c; - } - for at in AddressTypeSet::all() { - let a = aats.contains(at); - let b = bats.contains(at); - let c = a.cmp(&b); - if c != cmp::Ordering::Equal { - return c; - } - } - - // Compare by protocol types set second (fewer protocol types is less) - let c = apts.len().cmp(&bpts.len()); - if c != cmp::Ordering::Equal { - return c; - } - for pt in ProtocolTypeSet::all() { - let a = apts.contains(pt); - let b = bpts.contains(pt); - let c = a.cmp(&b); - if c != cmp::Ordering::Equal { - return c; - } - } - - // Then just compare by node id lists, so things are stable - let a_nodes = ardr.relay_node.node_ids().to_vec(); - let b_nodes = brdr.relay_node.node_ids().to_vec(); - - a_nodes.cmp(&b_nodes) - }); - relays - } - - /// Remove a relay's capabilities from our current requirements and determine which - /// pings should be performed. - /// Returns true if the requirements changed, or false if applying the relay had no effect - pub fn apply_relay(&mut self, mut relay: RoutingDomainRelay) -> bool { - // Make sure this relay is the correct routing domain and has peer info - let Some(relay_peer_info) = relay.relay_node.get_peer_info(self.routing_domain) else { - return false; - }; - - // Clear out the dial info details and the pings because we'll add new ones - relay.dial_info_details.clear(); - relay.pings.clear(); - - // For all for the relay's dial info, see if it matches a protocol+address type we need covered - let mut dial_info_list = relay_peer_info.node_info().dial_info_detail_list().to_vec(); - dial_info_list.sort_by(DialInfoDetail::ordered_sequencing_sort); - - // Determine for this relay, if there are dialinfo that are reachable with our node's - // dialinfo filter, and which ordering modes can be satisfied by those flows - - let mut possible_ordering_modes = HashSet::::new(); - for did in &dial_info_list { - if did.class.requires_signal() { - continue; - } - let didpa = (did.dial_info.protocol_type(), did.dial_info.address_type()); - - // If this dial info can be contacted directly, then it can be used for receiving - // relaying and satsifying an ordering mode - if did.dial_info.matches_filter(&self.dial_info_filter) { - possible_ordering_modes.insert(didpa.0.is_ordered()); - } - } - - // If we did not get a single ordering mode we need for relaying, then this relay is disqualified - // because we can't connect to it with our outbound protocols/address types directly - if possible_ordering_modes.is_empty() { - return false; - } - - let mut useful = false; - - // Determine relay dial infos we can use from this relay out of our set of needed relay combinations - // Builds up a set of needed ordering modes to keep flows open for the dial infos we are getting relayed - for did in &dial_info_list { - if did.class.requires_signal() { - continue; - } - let didpa = (did.dial_info.protocol_type(), did.dial_info.address_type()); - - if self.need_relay_protocols.remove(&didpa) { - // Still needed this protocol+address type - useful = true; - - // Mark this dial info as one we're using - relay.dial_info_details.push(did.clone()); - - // Mark this ordering mode as needed - self.need_relay_orderings - .remove(&(didpa.0.is_ordered(), didpa.1)); - } - } - - // Collect pings we can use from this relay - for did in &dial_info_list { - if did.class.requires_signal() { - continue; - } - let didpa = (did.dial_info.protocol_type(), did.dial_info.address_type()); - - // If this dial info can be contacted directly, then it is a ping candidate - if did.dial_info.matches_filter(&self.dial_info_filter) { - // See if we should add this ping - let mut add_ping = false; - - // See if we should add the ping for ordering mode coverage - let is_ordered = didpa.0.is_ordered(); - add_ping |= possible_ordering_modes.remove(&is_ordered); - - // See if we should add the ping for low level port mapping coverage - if let Some((llpt, port)) = self - .low_level_port_info - .protocol_to_port - .get(&didpa) - .copied() - { - let wnk = (llpt, didpa.1, port); - add_ping |= self.wants_nat_keepalives.remove(&wnk); - } - - // Add the ping if we determined we could use it - if add_ping { - relay.pings.push(RelayPing { - node_ref: relay.relay_node.unfiltered().custom_filtered( - NodeRefFilter::new() - .with_routing_domain(self.routing_domain) - .with_dial_info_filter(did.dial_info.make_filter()), - ), - }); - } - } - } - - // Add a relay info to our list if it turned out to be useful - if useful { - self.relays.push(relay); - } - - useful - } -} diff --git a/veilid-core/src/routing_table/tasks/closest_peers_refresh.rs b/veilid-core/src/routing_table/tasks/closest_peers_refresh.rs index 19d66ffc..8711e8bc 100644 --- a/veilid-core/src/routing_table/tasks/closest_peers_refresh.rs +++ b/veilid-core/src/routing_table/tasks/closest_peers_refresh.rs @@ -55,7 +55,7 @@ impl RoutingTable { let noderefs = self .find_preferred_closest_nodes( CLOSEST_PEERS_REQUEST_COUNT, - self_node_id.into(), + self_node_id.to_hash_coordinate(), filters, |_rti, entry: Option>| { NodeRef::new(self.registry(), entry.unwrap().clone()) diff --git a/veilid-core/src/routing_table/tasks/kick_buckets.rs b/veilid-core/src/routing_table/tasks/kick_buckets.rs index e8aef13b..27a14fd4 100644 --- a/veilid-core/src/routing_table/tasks/kick_buckets.rs +++ b/veilid-core/src/routing_table/tasks/kick_buckets.rs @@ -16,7 +16,6 @@ impl RoutingTable { _last_ts: Timestamp, cur_ts: Timestamp, ) -> EyreResult<()> { - let crypto = self.crypto(); let kick_queue: Vec = core::mem::take(&mut *self.kick_queue.lock()) .into_iter() .collect(); @@ -30,7 +29,8 @@ impl RoutingTable { let Some(buckets) = inner.buckets.get(&kind) else { continue; }; - let sort = make_closest_node_id_sort(&crypto, our_node_id.into()); + let sort = + make_closest_bare_node_id_sort(our_node_id.ref_value().to_bare_hash_coordinate()); let mut closest_peers = BTreeSet::::new(); let mut closest_unreliable_count = 0usize; diff --git a/veilid-core/src/routing_table/tasks/ping_validator.rs b/veilid-core/src/routing_table/tasks/ping_validator.rs index 9121c4a8..223e4787 100644 --- a/veilid-core/src/routing_table/tasks/ping_validator.rs +++ b/veilid-core/src/routing_table/tasks/ping_validator.rs @@ -274,10 +274,7 @@ impl RoutingTable { let ordering_modes = self .get_current_peer_info(RoutingDomain::PublicInternet) .node_info() - .outbound_protocols() - .iter() - .map(|x| x.is_ordered()) - .collect::>(); + .outbound_sequence_orderings(); // Just do a single ping with the best protocol for all the other nodes to check for liveness for nr in node_refs { @@ -286,11 +283,8 @@ impl RoutingTable { let all_noderefs = if nr.operate(|_rti, e| !relay_node_filter(e)) { let mut nrs = vec![]; - if ordering_modes.contains(&false) { - nrs.push(nr.sequencing_clone(Sequencing::NoPreference)); - } - if ordering_modes.contains(&true) { - nrs.push(nr.sequencing_clone(Sequencing::EnsureOrdered)); + for ordering in ordering_modes { + nrs.push(nr.sequencing_clone(ordering.strict_sequencing())); } nrs } else { diff --git a/veilid-core/src/routing_table/tasks/private_route_management.rs b/veilid-core/src/routing_table/tasks/private_route_management.rs index d66d0e98..d7d1620e 100644 --- a/veilid-core/src/routing_table/tasks/private_route_management.rs +++ b/veilid-core/src/routing_table/tasks/private_route_management.rs @@ -194,10 +194,7 @@ impl RoutingTable { let mut local_unpublished_route_count = 0usize; self.route_spec_store().list_allocated_routes(|_k, v| { - if !v.is_published() - && v.hop_count() == default_route_hop_count - && v.get_route_set_keys().kinds() == VALID_CRYPTO_KINDS - { + if !v.is_published() && v.hop_count() == default_route_hop_count { local_unpublished_route_count += 1; } Option::<()>::None diff --git a/veilid-core/src/routing_table/tasks/relay_management.rs b/veilid-core/src/routing_table/tasks/relay_management.rs index c8d2ea03..b7b4adcd 100644 --- a/veilid-core/src/routing_table/tasks/relay_management.rs +++ b/veilid-core/src/routing_table/tasks/relay_management.rs @@ -144,7 +144,7 @@ impl RoutingTable { if rdr .relay_node .node_ids() - .contains_any(outbound_relay_peerinfo.node_ids()) + .contains_any_from_slice(outbound_relay_peerinfo.node_ids()) { has_outbound_relay = true; true @@ -282,7 +282,7 @@ impl RoutingTable { // Exclude any nodes that are relaying directly through us if own_peer_info .node_ids() - .contains_any(&peer_info.node_info().relay_ids()) + .contains_any_from_slice(&peer_info.node_info().relay_ids()) { return false; } diff --git a/veilid-core/src/routing_table/tests/fixtures.rs b/veilid-core/src/routing_table/tests/fixtures.rs index 0170a8d3..93af6ddb 100644 --- a/veilid-core/src/routing_table/tests/fixtures.rs +++ b/veilid-core/src/routing_table/tests/fixtures.rs @@ -53,7 +53,7 @@ pub fn fix_typed_node_id_group(valid_kinds: bool, unknown: bool) -> NodeIdGroup }); } if unknown { - tks.add(fix_typed_node_id(CryptoKind([1, 2, 3, 4]), 0)); + tks.add(fix_typed_node_id(CryptoKind::new([1, 2, 3, 4]), 0)); } tks } @@ -129,7 +129,7 @@ pub fn fix_peer_info( ) -> VeilidAPIResult { let node_info = NodeInfo::new( Timestamp::new(0), - vec![ENVELOPE_VERSION_VLD0], + vec![ENVELOPE_VERSION_ENV0], crypto_info_list, PUBLIC_INTERNET_CAPABILITIES.to_vec(), ProtocolTypeSet::new(), @@ -152,7 +152,7 @@ pub fn fix_unsigned_peer_info( ) -> VeilidAPIResult { let node_info = NodeInfo::new( Timestamp::new(0), - vec![ENVELOPE_VERSION_VLD0], + vec![ENVELOPE_VERSION_ENV0], crypto_info_list, PUBLIC_INTERNET_CAPABILITIES.to_vec(), ProtocolTypeSet::new(), diff --git a/veilid-core/src/routing_table/types/hash_coordinate.rs b/veilid-core/src/routing_table/types/hash_coordinate.rs new file mode 100644 index 00000000..b26f6372 --- /dev/null +++ b/veilid-core/src/routing_table/types/hash_coordinate.rs @@ -0,0 +1,60 @@ +use super::*; + +pub const HASH_COORDINATE_LENGTH: usize = 32; + +// Internal types + +impl HashCoordinate { + pub(crate) fn distance(&self, other: &HashCoordinate) -> HashDistance { + assert_eq!(self.kind(), other.kind()); + self.ref_value().distance(other.ref_value()) + } +} + +impl NodeId { + pub(crate) fn to_hash_coordinate(&self) -> HashCoordinate { + HashCoordinate::new(self.kind(), self.ref_value().to_bare_hash_coordinate()) + } +} +impl BareNodeId { + pub(crate) fn to_bare_hash_coordinate(&self) -> BareHashCoordinate { + BareHashCoordinate::new(self) + } +} + +impl RecordKey { + pub(crate) fn to_hash_coordinate(&self) -> HashCoordinate { + HashCoordinate::new(self.kind(), self.ref_value().to_bare_hash_coordinate()) + } +} +impl BareRecordKey { + pub(crate) fn to_bare_hash_coordinate(&self) -> BareHashCoordinate { + BareHashCoordinate::new(self) + } +} + +impl HashDigest { + pub(crate) fn to_hash_coordinate(&self) -> HashCoordinate { + HashCoordinate::new(self.kind(), self.ref_value().to_bare_hash_coordinate()) + } +} +impl BareHashDigest { + pub(crate) fn to_bare_hash_coordinate(&self) -> BareHashCoordinate { + BareHashCoordinate::new(self) + } +} + +impl BareHashCoordinate { + pub(crate) fn distance(&self, other: &BareHashCoordinate) -> HashDistance { + assert_eq!(self.len(), HASH_COORDINATE_LENGTH); + assert_eq!(other.len(), HASH_COORDINATE_LENGTH); + + let mut bytes = [0u8; HASH_COORDINATE_LENGTH]; + + (0..HASH_COORDINATE_LENGTH).for_each(|n| { + bytes[n] = self[n] ^ other[n]; + }); + + HashDistance::new(&bytes) + } +} diff --git a/veilid-core/src/routing_table/types/mod.rs b/veilid-core/src/routing_table/types/mod.rs index 64ab858b..023973f2 100644 --- a/veilid-core/src/routing_table/types/mod.rs +++ b/veilid-core/src/routing_table/types/mod.rs @@ -5,6 +5,7 @@ mod direction; mod events; #[cfg(feature = "geolocation")] mod geolocation_info; +mod hash_coordinate; mod low_level_port_info; mod node_info; mod node_status; @@ -22,6 +23,7 @@ pub use direction::*; pub use events::*; #[cfg(feature = "geolocation")] pub use geolocation_info::*; +pub use hash_coordinate::*; pub use low_level_port_info::*; pub use node_info::*; pub use node_status::*; diff --git a/veilid-core/src/routing_table/types/node_info.rs b/veilid-core/src/routing_table/types/node_info.rs index 0a30a55e..757706fa 100644 --- a/veilid-core/src/routing_table/types/node_info.rs +++ b/veilid-core/src/routing_table/types/node_info.rs @@ -80,6 +80,13 @@ impl NodeInfo { pub fn outbound_protocols(&self) -> ProtocolTypeSet { self.outbound_protocols } + pub fn outbound_sequence_orderings(&self) -> SequenceOrderingSet { + self.outbound_protocols + .iter() + .map(|x| x.sequence_ordering()) + .collect::() + } + pub fn address_types(&self) -> AddressTypeSet { self.address_types } @@ -240,13 +247,8 @@ impl HasDialInfoDetailList for NodeInfo { fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool { // Check our dial info for did in self.dial_info_detail_list() { - match sequencing { - Sequencing::NoPreference | Sequencing::PreferOrdered => return true, - Sequencing::EnsureOrdered => { - if did.dial_info.protocol_type().is_ordered() { - return true; - } - } + if sequencing.matches_ordering(did.dial_info.protocol_type().sequence_ordering()) { + return true; } } // Check our relays diff --git a/veilid-core/src/routing_table/types/peer_info.rs b/veilid-core/src/routing_table/types/peer_info.rs index e3e5f10f..bedab37c 100644 --- a/veilid-core/src/routing_table/types/peer_info.rs +++ b/veilid-core/src/routing_table/types/peer_info.rs @@ -76,7 +76,7 @@ impl PeerInfo { let signatures = SignatureGroup::from(crypto.generate_signatures( &node_info_message, &keypairs, - |kp, sig| Signature::new(kp.kind(), sig), + |_kp, sig| sig, )?); // Extract node ids for convenience diff --git a/veilid-core/src/routing_table/types/relay_info.rs b/veilid-core/src/routing_table/types/relay_info.rs index bfd2b3a2..3555049e 100644 --- a/veilid-core/src/routing_table/types/relay_info.rs +++ b/veilid-core/src/routing_table/types/relay_info.rs @@ -89,13 +89,8 @@ impl HasDialInfoDetailList for RelayInfo { fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool { // Check our dial info for did in self.dial_info_detail_list() { - match sequencing { - Sequencing::NoPreference | Sequencing::PreferOrdered => return true, - Sequencing::EnsureOrdered => { - if did.dial_info.protocol_type().is_ordered() { - return true; - } - } + if sequencing.matches_ordering(did.dial_info.protocol_type().sequence_ordering()) { + return true; } } diff --git a/veilid-core/src/routing_table/types/veilid_capability.rs b/veilid-core/src/routing_table/types/veilid_capability.rs index 3a0dcb05..b0225380 100644 --- a/veilid-core/src/routing_table/types/veilid_capability.rs +++ b/veilid-core/src/routing_table/types/veilid_capability.rs @@ -1,16 +1,16 @@ use super::*; fourcc_type!(VeilidCapability); -pub const CAP_ROUTE: VeilidCapability = VeilidCapability(*b"ROUT"); +pub const CAP_ROUTE: VeilidCapability = VeilidCapability::new(*b"ROUT"); #[cfg(feature = "unstable-tunnels")] -pub const CAP_TUNNEL: VeilidCapability = VeilidCapability(*b"TUNL"); -pub const CAP_SIGNAL: VeilidCapability = VeilidCapability(*b"SGNL"); -pub const CAP_RELAY: VeilidCapability = VeilidCapability(*b"RLAY"); -pub const CAP_VALIDATE_DIAL_INFO: VeilidCapability = VeilidCapability(*b"DIAL"); -pub const CAP_DHT: VeilidCapability = VeilidCapability(*b"DHTV"); -pub const CAP_APPMESSAGE: VeilidCapability = VeilidCapability(*b"APPM"); +pub const CAP_TUNNEL: VeilidCapability = VeilidCapability::new(*b"TUNL"); +pub const CAP_SIGNAL: VeilidCapability = VeilidCapability::new(*b"SGNL"); +pub const CAP_RELAY: VeilidCapability = VeilidCapability::new(*b"RLAY"); +pub const CAP_VALIDATE_DIAL_INFO: VeilidCapability = VeilidCapability::new(*b"DIAL"); +pub const CAP_DHT: VeilidCapability = VeilidCapability::new(*b"DHTV"); +pub const CAP_APPMESSAGE: VeilidCapability = VeilidCapability::new(*b"APPM"); #[cfg(feature = "unstable-blockstore")] -pub const CAP_BLOCKSTORE: VeilidCapability = VeilidCapability(*b"BLOC"); +pub const CAP_BLOCKSTORE: VeilidCapability = VeilidCapability::new(*b"BLOC"); pub const DISTANCE_METRIC_CAPABILITIES: &[VeilidCapability] = &[CAP_DHT]; pub const CONNECTIVITY_CAPABILITIES: &[VeilidCapability] = diff --git a/veilid-core/src/rpc_processor/answer.rs b/veilid-core/src/rpc_processor/answer.rs index a7978b9c..4b495959 100644 --- a/veilid-core/src/rpc_processor/answer.rs +++ b/veilid-core/src/rpc_processor/answer.rs @@ -5,14 +5,14 @@ pub struct Answer { /// Hpw long it took to get this answer pub _latency: TimestampDuration, /// The private route requested to receive the reply - pub reply_private_route: Option, + pub reply_private_route: Option, /// The answer itself pub answer: T, } impl Answer { pub fn new( latency: TimestampDuration, - reply_private_route: Option, + reply_private_route: Option, answer: T, ) -> Self { Self { diff --git a/veilid-core/src/rpc_processor/coders/byte_array_types.rs b/veilid-core/src/rpc_processor/coders/byte_array_types.rs index bccd7025..59fc34c5 100644 --- a/veilid-core/src/rpc_processor/coders/byte_array_types.rs +++ b/veilid-core/src/rpc_processor/coders/byte_array_types.rs @@ -22,7 +22,7 @@ macro_rules! define_typed_byte_data_coder { $capnp_name: &$rust_name, builder: &mut veilid_capnp::$capnp_name::Builder, ) { - builder.set_kind(u32::from_be_bytes($capnp_name.kind().0)); + builder.set_kind(u32::from($capnp_name.kind())); builder.set_value($capnp_name.ref_value()); } } @@ -69,4 +69,4 @@ define_typed_byte_data_coder!(route_id, RouteId); define_typed_byte_data_coder!(signature, Signature); // Nonce -define_untyped_byte_data_coder!(nonce, BareNonce); +define_untyped_byte_data_coder!(nonce, Nonce); diff --git a/veilid-core/src/rpc_processor/coders/node_info.rs b/veilid-core/src/rpc_processor/coders/node_info.rs index 54405ac8..6e5e04c1 100644 --- a/veilid-core/src/rpc_processor/coders/node_info.rs +++ b/veilid-core/src/rpc_processor/coders/node_info.rs @@ -146,7 +146,8 @@ pub fn encode_node_info( let capvec: Vec = node_info .capabilities() .iter() - .map(|x| u32::from_be_bytes(x.0)) + .copied() + .map(u32::from) .collect(); s.clone_from_slice(&capvec); diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs index 33038a9c..a841775e 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs @@ -66,11 +66,7 @@ impl RPCOperationFindNodeQ { .reborrow() .init_capabilities(self.capabilities.len() as u32); if let Some(s) = cap_builder.as_slice() { - let capvec: Vec = self - .capabilities - .iter() - .map(|x| u32::from_be_bytes(x.0)) - .collect(); + let capvec: Vec = self.capabilities.iter().copied().map(u32::from).collect(); s.clone_from_slice(&capvec); } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_return_receipt.rs b/veilid-core/src/rpc_processor/coders/operations/operation_return_receipt.rs index 3c268546..8b0bac7c 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_return_receipt.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_return_receipt.rs @@ -7,10 +7,10 @@ pub(in crate::rpc_processor) struct RPCOperationReturnReceipt { impl RPCOperationReturnReceipt { pub fn new(receipt: Vec) -> Result { - if receipt.len() < MIN_RECEIPT_SIZE { + if receipt.len() < RCP0_MIN_RECEIPT_SIZE { return Err(RPCError::protocol("ReturnReceipt receipt too short to set")); } - if receipt.len() > MAX_RECEIPT_SIZE { + if receipt.len() > RCP0_MAX_RECEIPT_SIZE { return Err(RPCError::protocol("ReturnReceipt receipt too long to set")); } @@ -34,7 +34,7 @@ impl RPCOperationReturnReceipt { ) -> Result { rpc_ignore_missing_property!(reader, receipt); let rr = reader.get_receipt()?; - rpc_ignore_min_max_len!(rr, MIN_RECEIPT_SIZE, MAX_RECEIPT_SIZE); + rpc_ignore_min_max_len!(rr, RCP0_MIN_RECEIPT_SIZE, RCP0_MAX_RECEIPT_SIZE); Ok(Self { receipt: rr.to_vec(), diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_route.rs b/veilid-core/src/rpc_processor/coders/operations/operation_route.rs index b6fb33ab..b1979f87 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_route.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_route.rs @@ -5,7 +5,7 @@ pub(in crate::rpc_processor) struct RoutedOperation { routing_domain: RoutingDomain, sequencing: Sequencing, signatures: Vec, - nonce: BareNonce, + nonce: Nonce, data: Vec, } @@ -25,7 +25,7 @@ impl RoutedOperation { pub fn new( routing_domain: RoutingDomain, sequencing: Sequencing, - nonce: BareNonce, + nonce: Nonce, data: Vec, ) -> Self { Self { @@ -54,7 +54,7 @@ impl RoutedOperation { self.signatures.push(signature); } - pub fn nonce(&self) -> &BareNonce { + pub fn nonce(&self) -> &Nonce { &self.nonce } pub fn data(&self) -> &[u8] { diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_validate_dial_info.rs b/veilid-core/src/rpc_processor/coders/operations/operation_validate_dial_info.rs index 1b734ed2..678aebfc 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_validate_dial_info.rs @@ -9,12 +9,12 @@ pub(in crate::rpc_processor) struct RPCOperationValidateDialInfo { impl RPCOperationValidateDialInfo { pub fn new(dial_info: DialInfo, receipt: Vec, redirect: bool) -> Result { - if receipt.len() < MIN_RECEIPT_SIZE { + if receipt.len() < RCP0_MIN_RECEIPT_SIZE { return Err(RPCError::protocol( "ValidateDialInfo receipt too short to set", )); } - if receipt.len() > MAX_RECEIPT_SIZE { + if receipt.len() > RCP0_MAX_RECEIPT_SIZE { return Err(RPCError::protocol( "ValidateDialInfo receipt too long to set", )); @@ -53,7 +53,7 @@ impl RPCOperationValidateDialInfo { rpc_ignore_missing_property!(reader, receipt); let rcpt_reader = reader.get_receipt()?; - rpc_ignore_min_max_len!(rcpt_reader, MIN_RECEIPT_SIZE, MAX_RECEIPT_SIZE); + rpc_ignore_min_max_len!(rcpt_reader, RCP0_MIN_RECEIPT_SIZE, RCP0_MAX_RECEIPT_SIZE); let receipt = rcpt_reader.to_vec(); let redirect = reader.get_redirect(); diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs index 38fecffe..9d9fe56b 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs @@ -34,16 +34,9 @@ impl RPCOperationWatchValueQ { } let signature_data = Self::make_signature_data(&key, &subkeys, expiration, count, watch_id); - let signature = Signature::new( - vcrypto.kind(), - vcrypto - .sign( - watcher.ref_value().ref_key(), - watcher.ref_value().ref_secret(), - &signature_data, - ) - .map_err(RPCError::protocol)?, - ); + let signature = vcrypto + .sign(&watcher.key(), &watcher.secret(), &signature_data) + .map_err(RPCError::protocol)?; Ok(Self { key, @@ -51,7 +44,7 @@ impl RPCOperationWatchValueQ { expiration, count, watch_id, - watcher: PublicKey::new(watcher.kind(), watcher.ref_value().key()), + watcher: watcher.key(), signature, }) } @@ -68,7 +61,7 @@ impl RPCOperationWatchValueQ { let mut sig_data = Vec::with_capacity(key.ref_value().len() + 4 + (subkeys_ranges_len * 8) + 8 + 8); - sig_data.extend_from_slice(&key.kind().0); + sig_data.extend_from_slice(key.kind().bytes()); sig_data.extend_from_slice(key.ref_value()); for sk in subkeys.ranges() { sig_data.extend_from_slice(&sk.start().to_le_bytes()); @@ -96,11 +89,7 @@ impl RPCOperationWatchValueQ { self.watch_id, ); if !vcrypto - .verify( - self.watcher.ref_value(), - &sig_data, - self.signature.ref_value(), - ) + .verify(&self.watcher, &sig_data, &self.signature) .map_err(RPCError::protocol)? { return Err(RPCError::protocol("failed to validate watcher signature")); diff --git a/veilid-core/src/rpc_processor/destination.rs b/veilid-core/src/rpc_processor/destination.rs index cdc306a7..ff737180 100644 --- a/veilid-core/src/rpc_processor/destination.rs +++ b/veilid-core/src/rpc_processor/destination.rs @@ -391,7 +391,7 @@ impl RPCProcessor { .kind(); let mut avoid_nodes = relay.node_ids(); - avoid_nodes.add_all(&target.node_ids()); + avoid_nodes.add_all_from_slice(&target.node_ids()); let pr_key = network_result_try!(rss .get_private_route_for_safety_spec(crypto_kind, safety_spec, &avoid_nodes,) .to_rpc_network_result()?); @@ -430,7 +430,7 @@ impl RPCProcessor { // Determine if we can use optimized nodeinfo let route_node = if rss.has_remote_private_route_seen_our_node_info( - private_route.public_key.ref_value(), + &private_route.public_key, &published_peer_info, ) { RouteNode::NodeId(routing_table.node_id(crypto_kind)) @@ -440,7 +440,7 @@ impl RPCProcessor { Ok(NetworkResult::value(RespondTo::PrivateRoute( PrivateRoute::new_stub( - routing_table.node_id(crypto_kind).into(), + routing_table.public_key(crypto_kind), route_node, ), ))) @@ -450,12 +450,12 @@ impl RPCProcessor { // Check for loopback test let opt_private_route_id = - rss.get_route_id_for_key(private_route.public_key.ref_value()); + rss.get_route_id_for_key(&private_route.public_key); let pr_key = if opt_private_route_id.is_some() && safety_spec.preferred_route == opt_private_route_id { // Private route is also safety route during loopback test - private_route.public_key.value() + private_route.public_key.clone() } else { // Get the private route to respond to that matches the safety route spec we sent the request with network_result_try!(rss diff --git a/veilid-core/src/rpc_processor/error.rs b/veilid-core/src/rpc_processor/error.rs index c13ea161..064f2ea8 100644 --- a/veilid-core/src/rpc_processor/error.rs +++ b/veilid-core/src/rpc_processor/error.rs @@ -52,7 +52,6 @@ impl RPCError { pub fn map_network(message: M) -> impl FnOnce(X) -> Self { move |x| Self::Network(format!("{}: {}", message.to_string(), x.to_string())) } - #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), expect(dead_code))] pub fn try_again(x: X) -> Self { Self::TryAgain(x.to_string()) } diff --git a/veilid-core/src/rpc_processor/fanout/fanout_call.rs b/veilid-core/src/rpc_processor/fanout/fanout_call.rs index 2b3e23a4..628985f1 100644 --- a/veilid-core/src/rpc_processor/fanout/fanout_call.rs +++ b/veilid-core/src/rpc_processor/fanout/fanout_call.rs @@ -146,7 +146,7 @@ pub fn capability_fanout_peer_info_filter(caps: Vec) -> Fanout /// in the given time pub(crate) struct FanoutCall<'a> { routing_table: &'a RoutingTable, - hash_coordinate: HashDigest, + hash_coordinate: HashCoordinate, node_count: usize, fanout_tasks: usize, consensus_count: usize, @@ -166,7 +166,7 @@ impl<'a> FanoutCall<'a> { #[allow(clippy::too_many_arguments)] pub fn new( routing_table: &'a RoutingTable, - hash_coordinate: HashDigest, + hash_coordinate: HashCoordinate, node_count: usize, fanout_tasks: usize, consensus_count: usize, @@ -259,10 +259,7 @@ impl<'a> FanoutCall<'a> { } #[instrument(level = "trace", target = "fanout", skip_all)] - async fn fanout_processor<'b>( - &self, - context: &Mutex>, - ) -> Result { + async fn fanout_processor(&self, context: &Mutex>) -> Result { // Make a work request channel let (work_sender, work_receiver) = flume::bounded(1); @@ -407,27 +404,7 @@ impl<'a> FanoutCall<'a> { #[instrument(level = "trace", target = "fanout", skip_all)] pub async fn run(&self, init_fanout_queue: Vec) -> Result { // Create context for this run - let crypto = self.routing_table.crypto(); - let Some(vcrypto) = crypto.get(self.hash_coordinate.kind()) else { - return Err(RPCError::internal( - "should not try this on crypto we don't support", - )); - }; - let node_sort = Box::new( - |a_key: &CryptoTyped, - b_key: &CryptoTyped| - -> core::cmp::Ordering { - let da = vcrypto.distance( - &BareHashDigest::from(a_key.value()), - self.hash_coordinate.ref_value(), - ); - let db = vcrypto.distance( - &BareHashDigest::from(b_key.value()), - self.hash_coordinate.ref_value(), - ); - da.cmp(&db) - }, - ); + let node_sort = Box::new(make_closest_node_id_sort(self.hash_coordinate.clone())); let context = Arc::new(Mutex::new(FanoutContext { fanout_queue: FanoutQueue::new( self.routing_table.registry(), diff --git a/veilid-core/src/rpc_processor/message_header.rs b/veilid-core/src/rpc_processor/message_header.rs index 99006a91..81f45b7e 100644 --- a/veilid-core/src/rpc_processor/message_header.rs +++ b/veilid-core/src/rpc_processor/message_header.rs @@ -20,7 +20,7 @@ pub(in crate::rpc_processor) struct RPCMessageHeaderDetailSafetyRouted { /// Direct header pub direct: RPCMessageHeaderDetailDirect, /// Remote safety route used - pub remote_safety_route: BarePublicKey, + pub remote_safety_route: PublicKey, /// The sequencing used for this route pub sequencing: Sequencing, } @@ -31,9 +31,9 @@ pub(in crate::rpc_processor) struct RPCMessageHeaderDetailPrivateRouted { /// Direct header pub direct: RPCMessageHeaderDetailDirect, /// Remote safety route used (or possibly node id the case of no safety route) - pub remote_safety_route: BarePublicKey, + pub remote_safety_route: PublicKey, /// The private route we received the rpc over - pub private_route: BarePublicKey, + pub private_route: PublicKey, // The safety spec for replying to this private routed rpc pub safety_spec: SafetySpec, } diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index afbd2ae6..0d1856ac 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -80,9 +80,9 @@ struct WaitableReplyContext { node_ref: NodeRef, send_ts: Timestamp, send_data_result: SendDataResult, - safety_route: Option, - remote_private_route: Option, - reply_private_route: Option, + safety_route: Option, + remote_private_route: Option, + reply_private_route: Option, } #[derive(Debug)] @@ -434,7 +434,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); let fanout_call = FanoutCall::new( &routing_table, - node_id.into(), + node_id.to_hash_coordinate(), count, fanout, 0, @@ -570,10 +570,10 @@ impl RPCProcessor { )); } RPCMessageHeaderDetail::SafetyRouted(sr) => { - let node_id = self + let public_key = self .routing_table() - .node_id(sr.direct.envelope.get_crypto_kind()); - if node_id.value() != reply_private_route.into() { + .public_key(sr.direct.envelope.get_crypto_kind()); + if public_key != reply_private_route { return Err(RPCError::protocol( "should have received reply from safety route to a stub", )); @@ -600,7 +600,7 @@ impl RPCProcessor { routing_domain: RoutingDomain, safety_selection: SafetySelection, remote_private_route: PrivateRoute, - reply_private_route: Option, + reply_private_route: Option, message_data: Vec, ) -> RPCNetworkResult { let routing_table = self.routing_table(); @@ -609,7 +609,7 @@ impl RPCProcessor { // Get useful private route properties let pr_is_stub = remote_private_route.is_stub(); - let pr_pubkey = remote_private_route.public_key.value(); + let pr_pubkey = remote_private_route.public_key.clone(); let crypto_kind = remote_private_route.crypto_kind(); let Some(vcrypto) = crypto.get(crypto_kind) else { return Err(RPCError::internal( @@ -623,7 +623,7 @@ impl RPCProcessor { .compile_safety_route(safety_selection, remote_private_route) .to_rpc_network_result()?); let sr_is_stub = compiled_route.safety_route.is_stub(); - let sr_pubkey = compiled_route.safety_route.public_key.value(); + let sr_pubkey = compiled_route.safety_route.public_key.clone(); // Encrypt routed operation // Xmsg + ENC(Xmsg, DH(PKapr, SKbsr)) @@ -689,7 +689,7 @@ impl RPCProcessor { let reply_private_route = match operation.kind() { RPCOperationKind::Question(q) => match q.respond_to() { RespondTo::Sender => None, - RespondTo::PrivateRoute(pr) => Some(pr.public_key.value()), + RespondTo::PrivateRoute(pr) => Some(pr.public_key.clone()), }, RPCOperationKind::Statement(_) | RPCOperationKind::Answer(_) => None, }; @@ -759,8 +759,8 @@ impl RPCProcessor { Some(pi) => pi, }; let private_route = PrivateRoute::new_stub( - match destination_node_ref.best_node_id() { - Some(nid) => nid.into(), + match destination_node_ref.best_public_key(routing_domain) { + Some(pk) => pk, None => { return Ok(NetworkResult::no_connection_other( "No best node id for stub private route", @@ -859,8 +859,8 @@ impl RPCProcessor { rpc_kind: RPCKind, send_ts: Timestamp, node_ref: NodeRef, - safety_route: Option, - remote_private_route: Option, + safety_route: Option, + remote_private_route: Option, ) { let wants_answer = matches!(rpc_kind, RPCKind::Question); @@ -896,7 +896,7 @@ impl RPCProcessor { if context.safety_route.is_none() && context.remote_private_route.is_none() { context .node_ref - .stats_lost_answer(context.send_data_result.is_ordered()); + .stats_lost_answer(context.send_data_result.sequence_ordering()); // Also clear the last_connections for the entry so we make a new connection next time context.node_ref.clear_last_flows(); @@ -936,9 +936,9 @@ impl RPCProcessor { send_ts: Timestamp, bytes: ByteCount, node_ref: NodeRef, - safety_route: Option, - remote_private_route: Option, - ordered: bool, + safety_route: Option, + remote_private_route: Option, + ordering: SequenceOrdering, ) { // Record for node if this was not sent via a route if safety_route.is_none() && remote_private_route.is_none() { @@ -948,7 +948,7 @@ impl RPCProcessor { if is_answer { node_ref.stats_answer_sent(bytes); } else { - node_ref.stats_question_sent(send_ts, bytes, wants_answer, ordered); + node_ref.stats_question_sent(send_ts, bytes, wants_answer, ordering); } return; } @@ -990,7 +990,7 @@ impl RPCProcessor { context.send_ts, recv_ts, bytes, - context.send_data_result.is_ordered(), + context.send_data_result.sequence_ordering(), ); return; } @@ -1193,7 +1193,7 @@ impl RPCProcessor { node_ref.unfiltered(), safety_route.clone(), remote_private_route.clone(), - send_data_result.is_ordered(), + send_data_result.sequence_ordering(), ); // Ref the connection so it doesn't go away until we're done with the waitable reply @@ -1284,7 +1284,7 @@ impl RPCProcessor { node_ref.unfiltered(), safety_route, remote_private_route, - send_data_result.is_ordered(), + send_data_result.sequence_ordering(), ); Ok(NetworkResult::value(())) @@ -1357,7 +1357,7 @@ impl RPCProcessor { node_ref.unfiltered(), safety_route, remote_private_route, - send_data_result.is_ordered(), + send_data_result.sequence_ordering(), ); Ok(NetworkResult::value(())) diff --git a/veilid-core/src/rpc_processor/rendered_operation.rs b/veilid-core/src/rpc_processor/rendered_operation.rs index f217aec0..34a19d9b 100644 --- a/veilid-core/src/rpc_processor/rendered_operation.rs +++ b/veilid-core/src/rpc_processor/rendered_operation.rs @@ -9,11 +9,11 @@ pub struct RenderedOperation { /// Node to send envelope to (may not be destination node in case of relay) pub node_ref: FilteredNodeRef, /// The safety route used to send the message - pub safety_route: Option, + pub safety_route: Option, /// The private route used to send the message - pub remote_private_route: Option, + pub remote_private_route: Option, /// The private route requested to receive the reply - pub reply_private_route: Option, + pub reply_private_route: Option, } impl fmt::Debug for RenderedOperation { diff --git a/veilid-core/src/rpc_processor/rpc_find_node.rs b/veilid-core/src/rpc_processor/rpc_find_node.rs index 4095ba25..437dc257 100644 --- a/veilid-core/src/rpc_processor/rpc_find_node.rs +++ b/veilid-core/src/rpc_processor/rpc_find_node.rs @@ -120,7 +120,7 @@ impl RPCProcessor { let closest_nodes = network_result_try!(routing_table.find_preferred_closest_peers( routing_domain, - &node_id.clone().into(), + node_id.to_hash_coordinate(), &capabilities )); diff --git a/veilid-core/src/rpc_processor/rpc_get_value.rs b/veilid-core/src/rpc_processor/rpc_get_value.rs index 9c6a2bb0..17a9a3c1 100644 --- a/veilid-core/src/rpc_processor/rpc_get_value.rs +++ b/veilid-core/src/rpc_processor/rpc_get_value.rs @@ -141,10 +141,9 @@ impl RPCProcessor { } // Validate peers returned are, in fact, closer to the key than the node we sent this to - let valid = match RoutingTable::verify_peers_closer( - &vcrypto, - &target_node_id.clone().into(), - &record_key.clone().into(), + let valid = match self.routing_table().verify_peers_closer( + target_node_id.to_hash_coordinate(), + record_key.to_hash_coordinate(), &peers, ) { Ok(v) => v, @@ -226,7 +225,7 @@ impl RPCProcessor { let closer_to_key_peers = network_result_try!(routing_table .find_preferred_peers_closer_to_key( routing_domain, - &record_key.clone().into(), + record_key.to_hash_coordinate(), vec![CAP_DHT] )); diff --git a/veilid-core/src/rpc_processor/rpc_inspect_value.rs b/veilid-core/src/rpc_processor/rpc_inspect_value.rs index b210c978..16e482f0 100644 --- a/veilid-core/src/rpc_processor/rpc_inspect_value.rs +++ b/veilid-core/src/rpc_processor/rpc_inspect_value.rs @@ -138,10 +138,9 @@ impl RPCProcessor { } // Validate peers returned are, in fact, closer to the key than the node we sent this to - let valid = match RoutingTable::verify_peers_closer( - &vcrypto, - &target_node_id.clone().into(), - &record_key.clone().into(), + let valid = match self.routing_table().verify_peers_closer( + target_node_id.to_hash_coordinate(), + record_key.to_hash_coordinate(), &peers, ) { Ok(v) => v, @@ -214,7 +213,7 @@ impl RPCProcessor { let closer_to_key_peers = network_result_try!(routing_table .find_preferred_peers_closer_to_key( routing_domain, - &record_key.clone().into(), + record_key.to_hash_coordinate(), vec![CAP_DHT] )); diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index 9bcbb0a1..1af4d0e6 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -89,11 +89,8 @@ impl RPCProcessor { ) -> RPCNetworkResult<()> { // Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret) // xxx: punish nodes that send messages that fail to decrypt eventually? How to do this for safety routes? - let secret_key = self - .routing_table() - .secret_key(remote_sr_pubkey.kind()) - .value(); - let Ok(dh_secret) = vcrypto.cached_dh(remote_sr_pubkey.ref_value(), &secret_key) else { + let secret_key = self.routing_table().secret_key(remote_sr_pubkey.kind()); + let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey, &secret_key) else { return Ok(NetworkResult::invalid_message( "dh failed for remote safety route for safety routed operation", )); @@ -116,7 +113,7 @@ impl RPCProcessor { // Pass message to RPC system self.enqueue_safety_routed_message( detail, - remote_sr_pubkey.value(), + remote_sr_pubkey, routed_operation.sequencing(), body, ) @@ -146,7 +143,7 @@ impl RPCProcessor { // Ensure the route is validated, and construct a return safetyspec that matches the inbound preferences let routing_table = self.routing_table(); let rss = routing_table.route_spec_store(); - let preferred_route = rss.get_route_id_for_key(pr_pubkey.ref_value()); + let preferred_route = rss.get_route_id_for_key(&pr_pubkey); let Some((secret_key, safety_spec)) = rss.with_signature_validated_route( &pr_pubkey, @@ -172,7 +169,7 @@ impl RPCProcessor { // Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret) // xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes? - let Ok(dh_secret) = vcrypto.cached_dh(remote_sr_pubkey.ref_value(), &secret_key) else { + let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey, &secret_key) else { return Ok(NetworkResult::invalid_message( "dh failed for remote safety route for private routed operation", )); @@ -189,14 +186,8 @@ impl RPCProcessor { }; // Pass message to RPC system - self.enqueue_private_routed_message( - detail, - remote_sr_pubkey.value(), - pr_pubkey.value(), - safety_spec, - body, - ) - .map_err(RPCError::internal)?; + self.enqueue_private_routed_message(detail, remote_sr_pubkey, pr_pubkey, safety_spec, body) + .map_err(RPCError::internal)?; Ok(NetworkResult::value(())) } @@ -210,13 +201,9 @@ impl RPCProcessor { remote_sr_pubkey: PublicKey, pr_pubkey: PublicKey, ) -> RPCNetworkResult<()> { - // If the private route public key is our node id, then this was sent via safety route to our node directly + // If the private route public key is our public key, then this was sent via safety route to our node directly // so there will be no signatures to validate - if self - .routing_table() - .node_ids() - .contains(&pr_pubkey.clone().into()) - { + if self.routing_table().public_keys().contains(&pr_pubkey) { // The private route was a stub self.process_safety_routed_operation( detail, @@ -310,9 +297,9 @@ impl RPCProcessor { }; // Decrypt the blob with DEC(nonce, DH(the PR's public key, this hop's secret) - let secret_key = self.routing_table().secret_key(crypto_kind).value(); + let secret_key = self.routing_table().secret_key(crypto_kind); let dh_secret = vcrypto - .cached_dh(pr_pubkey.ref_value(), &secret_key) + .cached_dh(pr_pubkey, &secret_key) .map_err(RPCError::protocol)?; let dec_blob_data = match vcrypto.decrypt_aead( &route_hop_data.blob, @@ -343,18 +330,11 @@ impl RPCProcessor { // Sign the operation if this is not our last hop // as the last hop is already signed by the envelope if route_hop.next_hop.is_some() { - let node_id = self.routing_table().node_id(crypto_kind); - let secret_key = self.routing_table().secret_key(crypto_kind).value(); - let sig = Signature::new( - vcrypto.kind(), - vcrypto - .sign( - &node_id.value().into(), - &secret_key, - routed_operation.data(), - ) - .map_err(RPCError::internal)?, - ); + let public_key = self.routing_table().public_key(crypto_kind); + let secret_key = self.routing_table().secret_key(crypto_kind); + let sig = vcrypto + .sign(&public_key, &secret_key, routed_operation.data()) + .map_err(RPCError::internal)?; routed_operation.add_signature(sig); } @@ -413,10 +393,8 @@ impl RPCProcessor { // There is a safety route hop SafetyRouteHops::Data(ref route_hop_data) => { // Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret) - let secret_key = self.routing_table().secret_key(crypto_kind).value(); - let Ok(dh_secret) = - vcrypto.cached_dh(safety_route.public_key.ref_value(), &secret_key) - else { + let secret_key = self.routing_table().secret_key(crypto_kind); + let Ok(dh_secret) = vcrypto.cached_dh(&safety_route.public_key, &secret_key) else { return Ok(NetworkResult::invalid_message( "dh failed for safety route hop", )); diff --git a/veilid-core/src/rpc_processor/rpc_set_value.rs b/veilid-core/src/rpc_processor/rpc_set_value.rs index d87a8baa..96170efb 100644 --- a/veilid-core/src/rpc_processor/rpc_set_value.rs +++ b/veilid-core/src/rpc_processor/rpc_set_value.rs @@ -154,10 +154,9 @@ impl RPCProcessor { } // Validate peers returned are, in fact, closer to the key than the node we sent this to - let valid = match RoutingTable::verify_peers_closer( - &vcrypto, - &target_node_id.clone().into(), - &record_key.clone().into(), + let valid = match self.routing_table().verify_peers_closer( + target_node_id.to_hash_coordinate(), + record_key.to_hash_coordinate(), &peers, ) { Ok(v) => v, @@ -241,7 +240,7 @@ impl RPCProcessor { let closer_to_key_peers = network_result_try!(routing_table .find_preferred_peers_closer_to_key( routing_domain, - &record_key.clone().into(), + record_key.to_hash_coordinate(), vec![CAP_DHT] )); diff --git a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs index 307bd343..5cba504c 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -5,6 +5,7 @@ impl_veilid_log_facility!("rpc"); impl RPCProcessor { // Can only be sent directly, not via relays or routes #[instrument(level = "trace", target = "rpc", skip(self), ret, err(level=Level::DEBUG))] + #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), expect(dead_code))] pub async fn rpc_call_validate_dial_info( &self, peer: NodeRef, diff --git a/veilid-core/src/rpc_processor/rpc_value_changed.rs b/veilid-core/src/rpc_processor/rpc_value_changed.rs index fe4a450e..a0310987 100644 --- a/veilid-core/src/rpc_processor/rpc_value_changed.rs +++ b/veilid-core/src/rpc_processor/rpc_value_changed.rs @@ -62,10 +62,10 @@ impl RPCProcessor { "not processing value change over safety route", )); } - RPCMessageHeaderDetail::PrivateRouted(p) => NodeId::new( - p.direct.envelope.get_crypto_kind(), - p.remote_safety_route.clone().into(), - ), + RPCMessageHeaderDetail::PrivateRouted(p) => self + .routing_table() + .generate_node_id(&p.remote_safety_route) + .map_err(RPCError::internal)?, }; if debug_target_enabled!("dht") { diff --git a/veilid-core/src/rpc_processor/rpc_watch_value.rs b/veilid-core/src/rpc_processor/rpc_watch_value.rs index 29acd1d1..a8ecc074 100644 --- a/veilid-core/src/rpc_processor/rpc_watch_value.rs +++ b/veilid-core/src/rpc_processor/rpc_watch_value.rs @@ -153,10 +153,9 @@ impl RPCProcessor { } // Validate peers returned are, in fact, closer to the key than the node we sent this to - let valid = match RoutingTable::verify_peers_closer( - &vcrypto, - &target_node_id.clone().into(), - &record_key.clone().into(), + let valid = match self.routing_table().verify_peers_closer( + target_node_id.to_hash_coordinate(), + record_key.to_hash_coordinate(), &peers, ) { Ok(v) => v, @@ -266,7 +265,7 @@ impl RPCProcessor { let closer_to_key_peers = network_result_try!(routing_table .find_preferred_peers_closer_to_key( routing_domain, - &record_key.clone().into(), + record_key.to_hash_coordinate(), vec![CAP_DHT] )); diff --git a/veilid-core/src/rpc_processor/rpc_worker.rs b/veilid-core/src/rpc_processor/rpc_worker.rs index c915a8c9..7621a2dd 100644 --- a/veilid-core/src/rpc_processor/rpc_worker.rs +++ b/veilid-core/src/rpc_processor/rpc_worker.rs @@ -158,7 +158,7 @@ impl RPCProcessor { pub(super) fn enqueue_safety_routed_message( &self, direct: RPCMessageHeaderDetailDirect, - remote_safety_route: BarePublicKey, + remote_safety_route: PublicKey, sequencing: Sequencing, body: Vec, ) -> EyreResult<()> { @@ -203,8 +203,8 @@ impl RPCProcessor { pub(super) fn enqueue_private_routed_message( &self, direct: RPCMessageHeaderDetailDirect, - remote_safety_route: BarePublicKey, - private_route: BarePublicKey, + remote_safety_route: PublicKey, + private_route: PublicKey, safety_spec: SafetySpec, body: Vec, ) -> EyreResult<()> { diff --git a/veilid-core/src/storage_manager/get_value.rs b/veilid-core/src/storage_manager/get_value.rs index 6b34477d..c0418fff 100644 --- a/veilid-core/src/storage_manager/get_value.rs +++ b/veilid-core/src/storage_manager/get_value.rs @@ -259,7 +259,7 @@ impl StorageManager { let routing_table = registry.routing_table(); let fanout_call = FanoutCall::new( &routing_table, - record_key.clone().into(), + record_key.to_hash_coordinate(), key_count, fanout, consensus_count, @@ -374,18 +374,11 @@ impl StorageManager { return Ok(None); }; - // Get cryptosystem - let crypto = self.crypto(); - let Some(vcrypto) = crypto.get(record_key.kind()) else { - apibail_generic!("unsupported cryptosystem"); - }; - // Keep the list of nodes that returned a value for later reference let mut inner = self.inner.lock().await; Self::process_fanout_results_inner( &mut inner, - &vcrypto, record_key.clone(), core::iter::once((ValueSubkeyRangeSet::single(subkey), result.fanout_result)), false, diff --git a/veilid-core/src/storage_manager/inspect_value.rs b/veilid-core/src/storage_manager/inspect_value.rs index 0cbd07ed..094ed369 100644 --- a/veilid-core/src/storage_manager/inspect_value.rs +++ b/veilid-core/src/storage_manager/inspect_value.rs @@ -294,7 +294,7 @@ impl StorageManager { let routing_table = self.routing_table(); let fanout_call = FanoutCall::new( &routing_table, - record_key.into(), + record_key.to_hash_coordinate(), key_count, fanout, consensus_count, diff --git a/veilid-core/src/storage_manager/mod.rs b/veilid-core/src/storage_manager/mod.rs index 2d735243..7f8a7c79 100644 --- a/veilid-core/src/storage_manager/mod.rs +++ b/veilid-core/src/storage_manager/mod.rs @@ -198,7 +198,7 @@ impl StorageManager { for ck in VALID_CRYPTO_KINDS { let vcrypto = crypto.get(ck).unwrap(); let kp = vcrypto.generate_keypair(); - anonymous_watch_keys.add(KeyPair::new(ck, kp)); + anonymous_watch_keys.add(kp); } let inner = Self::new_inner(); @@ -530,11 +530,11 @@ impl StorageManager { schema_data: &[u8], ) -> RecordKey { let mut hash_data = Vec::::with_capacity(owner_key.len() + 4 + schema_data.len()); - hash_data.extend_from_slice(&vcrypto.kind().0); + hash_data.extend_from_slice(vcrypto.kind().bytes()); hash_data.extend_from_slice(owner_key); hash_data.extend_from_slice(schema_data); let hash = vcrypto.generate_hash(&hash_data); - RecordKey::new(vcrypto.kind(), BareRecordKey::from(hash)) + RecordKey::new(vcrypto.kind(), BareRecordKey::new(hash.ref_value())) } /// Create a local record from scratch with a new owner key, open it, and return the opened descriptor @@ -894,7 +894,7 @@ impl StorageManager { &descriptor.owner(), subkey, &vcrypto, - &writer.bare_secret(), + &writer.secret(), )?); // Check if we are offline @@ -1293,12 +1293,6 @@ impl StorageManager { subkeys }; - // Get cryptosystem - let crypto = self.crypto(); - let Some(vcrypto) = crypto.get(record_key.kind()) else { - apibail_generic!("unsupported cryptosystem"); - }; - let mut inner = self.inner.lock().await; let safety_selection = { let Some(opened_record) = inner.opened_records.get(&record_key) else { @@ -1390,7 +1384,6 @@ impl StorageManager { Self::process_fanout_results_inner( &mut inner, - &vcrypto, record_key.clone(), results_iter, false, @@ -1552,7 +1545,7 @@ impl StorageManager { } owner } else { - KeyPair::new(vcrypto.kind(), vcrypto.generate_keypair()) + vcrypto.generate_keypair() }; // Calculate dht key @@ -1563,7 +1556,7 @@ impl StorageManager { owner.key(), schema_data, &vcrypto, - owner.bare_secret(), + owner.secret(), )?); // Add new local value record @@ -1704,7 +1697,7 @@ impl StorageManager { // Otherwise this is just another subkey writer let owner_secret = if let Some(writer) = writer.clone() { if writer.key() == owner { - Some(writer.bare_secret()) + Some(writer.secret()) } else { None } @@ -1753,7 +1746,7 @@ impl StorageManager { // Otherwise this is just another subkey writer let owner_secret = if let Some(writer) = &writer { if writer.key() == owner { - Some(writer.bare_secret()) + Some(writer.secret()) } else { None } @@ -1807,12 +1800,7 @@ impl StorageManager { d.nodes .keys() .cloned() - .filter_map(|x| { - routing_table - .lookup_node_ref(NodeId::new(record_key.kind(), x)) - .ok() - .flatten() - }) + .filter_map(|nr| routing_table.lookup_node_ref(nr).ok().flatten()) .collect() }); @@ -1822,7 +1810,6 @@ impl StorageManager { #[instrument(level = "trace", target = "stor", skip_all)] fn process_fanout_results_inner>( inner: &mut StorageManagerInner, - vcrypto: &CryptoSystemGuard<'_>, record_key: RecordKey, subkey_results_iter: I, is_set: bool, @@ -1839,7 +1826,7 @@ impl StorageManager { for node_id in fanout_result .value_nodes .iter() - .filter_map(|x| x.node_ids().get(record_key.kind()).map(|k| k.value())) + .filter_map(|x| x.node_ids().get(record_key.kind())) { let pnd = d.nodes.entry(node_id).or_default(); if is_set || pnd.last_set == Timestamp::default() { @@ -1863,14 +1850,13 @@ impl StorageManager { return res; } // Distance is the next metric, closer nodes first - let da = vcrypto.distance( - &BareHashDigest::from(a.0.clone()), - &BareHashDigest::from(record_key.value()), - ); - let db = vcrypto.distance( - &BareHashDigest::from(b.0.clone()), - &BareHashDigest::from(record_key.value()), - ); + let da = + a.0.to_hash_coordinate() + .distance(&record_key.to_hash_coordinate()); + + let db = + b.0.to_hash_coordinate() + .distance(&record_key.to_hash_coordinate()); da.cmp(&db) }); @@ -1969,7 +1955,7 @@ impl StorageManager { } #[instrument(level = "trace", target = "stor", skip_all, err)] - pub(super) async fn handle_inspect_local_value_inner( + async fn handle_inspect_local_value_inner( &self, inner: &mut StorageManagerInner, record_key: RecordKey, @@ -1998,7 +1984,7 @@ impl StorageManager { } #[instrument(level = "trace", target = "stor", skip_all, err)] - pub(super) async fn handle_get_remote_value_inner( + async fn handle_get_remote_value_inner( inner: &mut StorageManagerInner, record_key: RecordKey, subkey: ValueSubkey, diff --git a/veilid-core/src/storage_manager/outbound_watch_manager/outbound_watch_state.rs b/veilid-core/src/storage_manager/outbound_watch_manager/outbound_watch_state.rs index c12bab04..630f56db 100644 --- a/veilid-core/src/storage_manager/outbound_watch_manager/outbound_watch_state.rs +++ b/veilid-core/src/storage_manager/outbound_watch_manager/outbound_watch_state.rs @@ -15,7 +15,7 @@ pub(in crate::storage_manager) struct OutboundWatchState { /// Calculated field: minimum expiration time for all our nodes min_expiration_ts: Timestamp, /// Calculated field: the set of value changed routes for this watch from all per node watches - value_changed_routes: BTreeSet, + value_changed_routes: BTreeSet, } impl fmt::Display for OutboundWatchState { @@ -122,7 +122,7 @@ impl OutboundWatchState { pub fn min_expiration_ts(&self) -> Timestamp { self.min_expiration_ts } - pub fn value_changed_routes(&self) -> &BTreeSet { + pub fn value_changed_routes(&self) -> &BTreeSet { &self.value_changed_routes } diff --git a/veilid-core/src/storage_manager/outbound_watch_manager/per_node_state.rs b/veilid-core/src/storage_manager/outbound_watch_manager/per_node_state.rs index a3de08ba..659e0352 100644 --- a/veilid-core/src/storage_manager/outbound_watch_manager/per_node_state.rs +++ b/veilid-core/src/storage_manager/outbound_watch_manager/per_node_state.rs @@ -45,7 +45,7 @@ pub(in crate::storage_manager) struct PerNodeState { #[serde(skip)] pub watch_node_ref: Option, /// Which private route is responsible for receiving ValueChanged notifications - pub opt_value_changed_route: Option, + pub opt_value_changed_route: Option, } impl fmt::Display for PerNodeState { diff --git a/veilid-core/src/storage_manager/record_store/local_record_detail.rs b/veilid-core/src/storage_manager/record_store/local_record_detail.rs index ad43c5cd..8a6d0b95 100644 --- a/veilid-core/src/storage_manager/record_store/local_record_detail.rs +++ b/veilid-core/src/storage_manager/record_store/local_record_detail.rs @@ -16,7 +16,7 @@ pub(in crate::storage_manager) struct LocalRecordDetail { pub safety_selection: SafetySelection, /// The nodes that we have seen this record cached on recently #[serde(default)] - pub nodes: HashMap, + pub nodes: HashMap, } impl LocalRecordDetail { diff --git a/veilid-core/src/storage_manager/rehydrate.rs b/veilid-core/src/storage_manager/rehydrate.rs index 99eec59a..b2e787b2 100644 --- a/veilid-core/src/storage_manager/rehydrate.rs +++ b/veilid-core/src/storage_manager/rehydrate.rs @@ -231,12 +231,6 @@ impl StorageManager { ) -> VeilidAPIResult { let mut inner = self.inner.lock().await; - // Get cryptosystem - let crypto = self.crypto(); - let Some(vcrypto) = crypto.get(record_key.kind()) else { - apibail_generic!("unsupported cryptosystem"); - }; - // For each subkey, determine if we should rehydrate it let mut rehydrated = ValueSubkeyRangeSet::new(); for (n, subkey) in local_inspect_result.subkeys().iter().enumerate() { @@ -282,7 +276,6 @@ impl StorageManager { Self::process_fanout_results_inner( &mut inner, - &vcrypto, record_key.clone(), results_iter, false, diff --git a/veilid-core/src/storage_manager/schema.rs b/veilid-core/src/storage_manager/schema.rs index 39a5e4ae..2d465617 100644 --- a/veilid-core/src/storage_manager/schema.rs +++ b/veilid-core/src/storage_manager/schema.rs @@ -16,13 +16,13 @@ impl StorageManager { let idhash = vcrypto.generate_hash(writer_key.ref_value()); assert!( - idhash.len() >= MEMBER_ID_LENGTH, + idhash.ref_value().len() >= MEMBER_ID_LENGTH, "generate_hash needs to produce at least {} bytes", MEMBER_ID_LENGTH ); Ok(MemberId::new( writer_key.kind(), - BareMemberId::new(&idhash[0..MEMBER_ID_LENGTH]), + BareMemberId::new(&idhash.ref_value()[0..MEMBER_ID_LENGTH]), )) } diff --git a/veilid-core/src/storage_manager/set_value.rs b/veilid-core/src/storage_manager/set_value.rs index 6137e589..89518503 100644 --- a/veilid-core/src/storage_manager/set_value.rs +++ b/veilid-core/src/storage_manager/set_value.rs @@ -232,7 +232,7 @@ impl StorageManager { let routing_table = registry.routing_table(); let fanout_call = FanoutCall::new( &routing_table, - record_key.into(), + record_key.to_hash_coordinate(), key_count, fanout, consensus_count, @@ -356,12 +356,6 @@ impl StorageManager { safety_selection: SafetySelection, result: set_value::OutboundSetValueResult, ) -> Result, VeilidAPIError> { - // Get cryptosystem - let crypto = self.crypto(); - let Some(vcrypto) = crypto.get(record_key.kind()) else { - apibail_generic!("unsupported cryptosystem"); - }; - // Regain the lock after network access let mut inner = self.inner.lock().await; @@ -382,7 +376,6 @@ impl StorageManager { // Keep the list of nodes that returned a value for later reference Self::process_fanout_results_inner( &mut inner, - &vcrypto, record_key.clone(), core::iter::once((ValueSubkeyRangeSet::single(subkey), result.fanout_result)), true, diff --git a/veilid-core/src/storage_manager/tasks/offline_subkey_writes.rs b/veilid-core/src/storage_manager/tasks/offline_subkey_writes.rs index b7d224c2..92a0f220 100644 --- a/veilid-core/src/storage_manager/tasks/offline_subkey_writes.rs +++ b/veilid-core/src/storage_manager/tasks/offline_subkey_writes.rs @@ -189,12 +189,8 @@ impl StorageManager { ); // Keep the list of nodes that returned a value for later reference - let crypto = self.crypto(); - let vcrypto = crypto.get(result.work_item.record_key.kind()).unwrap(); - Self::process_fanout_results_inner( &mut inner, - &vcrypto, result.work_item.record_key, result.fanout_results.into_iter().map(|x| (x.0, x.1)), true, diff --git a/veilid-core/src/storage_manager/types/signed_value_data.rs b/veilid-core/src/storage_manager/types/signed_value_data.rs index 8c03d7b6..b116554a 100644 --- a/veilid-core/src/storage_manager/types/signed_value_data.rs +++ b/veilid-core/src/storage_manager/types/signed_value_data.rs @@ -30,9 +30,9 @@ impl SignedValueData { } // validate signature vcrypto.verify( - self.value_data.ref_writer().ref_value(), + self.value_data.ref_writer(), &node_info_bytes, - self.signature.ref_value(), + &self.signature, ) } @@ -41,19 +41,12 @@ impl SignedValueData { owner: &PublicKey, subkey: ValueSubkey, vcrypto: &CryptoSystemGuard<'_>, - writer_secret: &BareSecretKey, + writer_secret: &SecretKey, ) -> VeilidAPIResult { let node_info_bytes = Self::make_signature_bytes(&value_data, owner, subkey)?; // create signature - let signature = Signature::new( - vcrypto.kind(), - vcrypto.sign( - value_data.ref_writer().ref_value(), - writer_secret, - &node_info_bytes, - )?, - ); + let signature = vcrypto.sign(value_data.ref_writer(), writer_secret, &node_info_bytes)?; Ok(Self { value_data, signature, @@ -94,7 +87,7 @@ impl SignedValueData { // Add owner to signature if !is_vld0 { - node_info_bytes.extend_from_slice(&owner.kind().0); + node_info_bytes.extend_from_slice(owner.kind().bytes()); } node_info_bytes.extend_from_slice(owner.ref_value()); // Add subkey to signature diff --git a/veilid-core/src/storage_manager/types/signed_value_descriptor.rs b/veilid-core/src/storage_manager/types/signed_value_descriptor.rs index 3fc927f1..5666d830 100644 --- a/veilid-core/src/storage_manager/types/signed_value_descriptor.rs +++ b/veilid-core/src/storage_manager/types/signed_value_descriptor.rs @@ -33,11 +33,7 @@ impl SignedValueDescriptor { ); } // validate signature - if !vcrypto.verify( - self.owner.ref_value(), - &self.schema_data, - self.signature.ref_value(), - )? { + if !vcrypto.verify(&self.owner, &self.schema_data, &self.signature)? { apibail_parse_error!( "failed to validate signature of signed value descriptor", &self.signature @@ -77,7 +73,7 @@ impl SignedValueDescriptor { owner: PublicKey, schema_data: Vec, vcrypto: &CryptoSystemGuard<'_>, - owner_secret: BareSecretKey, + owner_secret: SecretKey, ) -> VeilidAPIResult { if owner.kind() != vcrypto.kind() { apibail_parse_error!( @@ -86,10 +82,7 @@ impl SignedValueDescriptor { ); } // create signature - let signature = Signature::new( - vcrypto.kind(), - vcrypto.sign(owner.ref_value(), &owner_secret, &schema_data)?, - ); + let signature = vcrypto.sign(&owner, &owner_secret, &schema_data)?; Ok(Self { owner, schema_data, diff --git a/veilid-core/src/storage_manager/watch_value.rs b/veilid-core/src/storage_manager/watch_value.rs index e5d2142b..30880f19 100644 --- a/veilid-core/src/storage_manager/watch_value.rs +++ b/veilid-core/src/storage_manager/watch_value.rs @@ -20,7 +20,7 @@ pub(super) struct AcceptedWatch { /// The expiration of a successful watch pub expiration_ts: Timestamp, /// Which private route is responsible for receiving ValueChanged notifications - pub opt_value_changed_route: Option, + pub opt_value_changed_route: Option, } /// The result of the outbound_watch_value operation @@ -327,7 +327,7 @@ impl StorageManager { let routing_table = self.routing_table(); let fanout_call = FanoutCall::new( &routing_table, - record_key.clone().into(), + record_key.to_hash_coordinate(), key_count, fanout, consensus_count, @@ -344,17 +344,10 @@ impl StorageManager { veilid_log!(self debug target:"dht", "WatchValue Fanout: {:#}", fanout_result); - // Get cryptosystem - let crypto = self.crypto(); - let Some(vcrypto) = crypto.get(record_key.kind()) else { - apibail_generic!("unsupported cryptosystem"); - }; - // Keep the list of nodes that responded for later reference let mut inner = self.inner.lock().await; Self::process_fanout_results_inner( &mut inner, - &vcrypto, record_key, core::iter::once((ValueSubkeyRangeSet::new(), fanout_result)), false, diff --git a/veilid-core/src/table_store/mod.rs b/veilid-core/src/table_store/mod.rs index 02268495..1a1aa85c 100644 --- a/veilid-core/src/table_store/mod.rs +++ b/veilid-core/src/table_store/mod.rs @@ -292,7 +292,7 @@ impl TableStore { } let protected_key = &dek_bytes[4..(4 + vcrypto.shared_secret_length() + vcrypto.aead_overhead())]; - let nonce = BareNonce::new( + let nonce = Nonce::new( &dek_bytes[(4 + vcrypto.shared_secret_length() + vcrypto.aead_overhead())..], ); let shared_secret = vcrypto @@ -352,7 +352,7 @@ impl TableStore { let mut out = Vec::with_capacity( 4 + vcrypto.shared_secret_length() + vcrypto.aead_overhead() + vcrypto.nonce_length(), ); - out.extend_from_slice(&dek.kind().0); + out.extend_from_slice(dek.kind().bytes()); out.extend_from_slice(&protected_key); out.extend_from_slice(&nonce); assert_eq!( @@ -457,7 +457,7 @@ impl TableStore { let vcrypto = crypto.best_async(); let shared_secret = vcrypto.random_shared_secret().await; - device_encryption_key = Some(SharedSecret::new(vcrypto.kind(), shared_secret)); + device_encryption_key = Some(shared_secret); device_encryption_key_changed = true; } diff --git a/veilid-core/src/table_store/table_db.rs b/veilid-core/src/table_store/table_db.rs index 14e30e57..1c479238 100644 --- a/veilid-core/src/table_store/table_db.rs +++ b/veilid-core/src/table_store/table_db.rs @@ -139,7 +139,7 @@ impl TableDB { let mut noncedata = Vec::with_capacity(data.len() + ei.secret.ref_value().len()); noncedata.extend_from_slice(&data); noncedata.extend_from_slice(ei.secret.ref_value()); - let noncehash = vcrypto.generate_hash(&noncedata); + let noncehash = vcrypto.generate_hash(&noncedata).value(); // Key content nonce is first 'nonce_length' bytes of generated hash out[0..vcrypto.nonce_length()] .copy_from_slice(&noncehash[0..vcrypto.nonce_length()]); @@ -150,7 +150,7 @@ impl TableDB { let (nonce, encout) = out.split_at_mut(vcrypto.nonce_length()); vcrypto - .crypt_b2b_no_auth(&data, encout, &BareNonce::new(nonce), ei.secret.ref_value()) + .crypt_b2b_no_auth(&data, encout, &Nonce::new(nonce), &ei.secret) .unwrap(); out } else { @@ -175,8 +175,8 @@ impl TableDB { .crypt_b2b_no_auth( &data[vcrypto.nonce_length()..], &mut out, - &BareNonce::new(&data[0..vcrypto.nonce_length()]), - di.secret.ref_value(), + &Nonce::new(&data[0..vcrypto.nonce_length()]), + &di.secret, ) .unwrap(); decompress_size_prepended(&out, None).map_err(|e| std::io::Error::other(e.to_string())) diff --git a/veilid-core/src/table_store/tests/test_table_store.rs b/veilid-core/src/table_store/tests/test_table_store.rs index 109d4440..43e5cd48 100644 --- a/veilid-core/src/table_store/tests/test_table_store.rs +++ b/veilid-core/src/table_store/tests/test_table_store.rs @@ -172,9 +172,9 @@ pub async fn test_json(vcrypto: &AsyncCryptoSystemGuard<'_>, ts: &TableStore) { assert!(db.store_json(0, b"asdf", &keypair).await.is_ok()); - assert_eq!(db.load_json::(0, b"qwer").await.unwrap(), None); + assert_eq!(db.load_json::(0, b"qwer").await.unwrap(), None); - let d = match db.load_json::(0, b"asdf").await { + let d = match db.load_json::(0, b"asdf").await { Ok(x) => x, Err(e) => { panic!("couldn't decode: {}", e); @@ -182,7 +182,7 @@ pub async fn test_json(vcrypto: &AsyncCryptoSystemGuard<'_>, ts: &TableStore) { }; assert_eq!(d, Some(keypair.clone()), "keys should be equal"); - let d = match db.delete_json::(0, b"asdf").await { + let d = match db.delete_json::(0, b"asdf").await { Ok(x) => x, Err(e) => { panic!("couldn't decode: {}", e); diff --git a/veilid-core/src/tests/common/test_dht.rs b/veilid-core/src/tests/common/test_dht.rs index d20420cb..b5353e60 100644 --- a/veilid-core/src/tests/common/test_dht.rs +++ b/veilid-core/src/tests/common/test_dht.rs @@ -5,10 +5,8 @@ use crate::*; use lazy_static::*; lazy_static! { - static ref BOGUS_KEY: RecordKey = RecordKey::from(CryptoTyped::new( - TEST_CRYPTO_KIND, - BareRecordKey::new(&[0u8; 32]) - )); + static ref BOGUS_KEY: RecordKey = + RecordKey::new(TEST_CRYPTO_KIND, BareRecordKey::new(&[0u8; 32])); } pub async fn test_get_dht_value_unopened(api: VeilidAPI) { @@ -43,7 +41,7 @@ pub async fn test_create_delete_dht_record_simple(api: VeilidAPI) { let rc = api.routing_context().unwrap(); let rec = rc - .create_dht_record(DHTSchema::dflt(1).unwrap(), None, Some(TEST_CRYPTO_KIND)) + .create_dht_record(TEST_CRYPTO_KIND, DHTSchema::dflt(1).unwrap(), None) .await .unwrap(); @@ -57,13 +55,13 @@ pub async fn test_create_dht_record_with_owner(api: VeilidAPI) { let crypto = api.crypto().unwrap(); let cs = crypto.get(TEST_CRYPTO_KIND).unwrap(); - let owner_keypair = KeyPair::new(cs.kind(), cs.generate_keypair()); + let owner_keypair = cs.generate_keypair(); let rec = rc .create_dht_record( + TEST_CRYPTO_KIND, DHTSchema::dflt(1).unwrap(), Some(owner_keypair.clone()), - Some(TEST_CRYPTO_KIND), ) .await .unwrap(); @@ -80,15 +78,15 @@ pub async fn test_get_dht_record_key(api: VeilidAPI) { let crypto = api.crypto().unwrap(); let cs = crypto.get(TEST_CRYPTO_KIND).unwrap(); - let owner_keypair = KeyPair::new(cs.kind(), cs.generate_keypair()); + let owner_keypair = cs.generate_keypair(); let schema = DHTSchema::dflt(1).unwrap(); // create the record normally let rec = rc .create_dht_record( + TEST_CRYPTO_KIND, schema.clone(), Some(owner_keypair.clone()), - Some(TEST_CRYPTO_KIND), ) .await .unwrap(); @@ -110,7 +108,7 @@ pub async fn test_get_dht_value_nonexistent(api: VeilidAPI) { let rc = api.routing_context().unwrap(); let rec = rc - .create_dht_record(DHTSchema::dflt(1).unwrap(), None, Some(TEST_CRYPTO_KIND)) + .create_dht_record(TEST_CRYPTO_KIND, DHTSchema::dflt(1).unwrap(), None) .await .unwrap(); let dht_key = rec.key(); @@ -125,7 +123,7 @@ pub async fn test_set_get_dht_value(api: VeilidAPI) { let rc = api.routing_context().unwrap(); let rec = rc - .create_dht_record(DHTSchema::dflt(2).unwrap(), None, Some(TEST_CRYPTO_KIND)) + .create_dht_record(TEST_CRYPTO_KIND, DHTSchema::dflt(2).unwrap(), None) .await .unwrap(); let dht_key = rec.key(); @@ -175,13 +173,13 @@ pub async fn test_open_writer_dht_value(api: VeilidAPI) { let rc = api.routing_context().unwrap(); let rec = rc - .create_dht_record(DHTSchema::dflt(2).unwrap(), None, Some(TEST_CRYPTO_KIND)) + .create_dht_record(TEST_CRYPTO_KIND, DHTSchema::dflt(2).unwrap(), None) .await .unwrap(); let key = rec.key(); let owner = rec.owner(); let secret = rec.owner_secret().unwrap(); - let keypair = KeyPair::new_from_parts(owner.clone(), secret.clone()); + let keypair = KeyPair::new_from_parts(owner.clone(), secret.value()); let test_value_1 = String::from("Qwertyuiop Asdfghjkl Zxcvbnm") .as_bytes() @@ -273,7 +271,7 @@ pub async fn test_open_writer_dht_value(api: VeilidAPI) { assert_eq!(rec.ref_key().kind(), key.kind()); assert_eq!(rec.owner(), owner); assert_eq!(rec.owner_secret().unwrap(), secret); - assert_eq!(rec.schema(), &DHTSchema::dflt(2).unwrap()); + assert_eq!(rec.ref_schema(), &DHTSchema::dflt(2).unwrap()); //Verify subkey 1 can be set before it is get but newer is available online let set_dht_test_value_1_result = rc @@ -314,8 +312,10 @@ pub async fn test_open_writer_dht_value(api: VeilidAPI) { let crypto = api.crypto().unwrap(); let cs = crypto.get(key.kind()).unwrap(); - assert!(cs.validate_keypair(owner.ref_value(), &secret)); - let other_keypair = KeyPair::new(cs.kind(), cs.generate_keypair()); + assert!(cs + .validate_keypair(&owner, &secret) + .expect("should succeed")); + let other_keypair = cs.generate_keypair(); let rec = rc.open_dht_record(key.clone(), Some(other_keypair)).await; assert!(rec.is_ok()); @@ -324,7 +324,7 @@ pub async fn test_open_writer_dht_value(api: VeilidAPI) { assert_eq!(rec.ref_key().kind(), key.kind()); assert_eq!(rec.owner(), owner); assert_eq!(rec.owner_secret(), None); - assert_eq!(rec.schema(), &DHTSchema::dflt(2).unwrap()); + assert_eq!(rec.ref_schema(), &DHTSchema::dflt(2).unwrap()); // Verify subkey 1 can NOT be set because we have the wrong writer let set_dht_test_value_0_result = rc @@ -361,7 +361,7 @@ pub async fn test_set_dht_value_allow_offline(api: VeilidAPI) { // Create a DHT record let rec = rc - .create_dht_record(DHTSchema::dflt(1).unwrap(), None, Some(CRYPTO_KIND_VLD0)) + .create_dht_record(CRYPTO_KIND_VLD0, DHTSchema::dflt(1).unwrap(), None) .await .unwrap(); let dht_key = rec.key(); diff --git a/veilid-core/src/veilid_api/api.rs b/veilid-core/src/veilid_api/api.rs index 50358d0d..16d47089 100644 --- a/veilid-core/src/veilid_api/api.rs +++ b/veilid-core/src/veilid_api/api.rs @@ -242,7 +242,7 @@ impl VeilidAPI { /// Returns a route id and 'blob' that can be published over some means (DHT or otherwise) to be /// imported by another Veilid node. //#[instrument(target = "veilid_api", level = "debug", skip(self), ret, err)] - pub async fn new_private_route(&self) -> VeilidAPIResult<(RouteId, Vec)> { + pub async fn new_private_route(&self) -> VeilidAPIResult { Box::pin(self.new_custom_private_route( &VALID_CRYPTO_KINDS, Stability::Reliable, @@ -265,7 +265,7 @@ impl VeilidAPI { crypto_kinds: &[CryptoKind], stability: Stability, sequencing: Sequencing, - ) -> VeilidAPIResult<(RouteId, Vec)> { + ) -> VeilidAPIResult { veilid_log!(self debug "VeilidAPI::new_custom_private_route(crypto_kinds: {:?}, stability: {:?}, sequencing: {:?})", crypto_kinds, @@ -317,7 +317,7 @@ impl VeilidAPI { rss.mark_route_published(&route_id, true)?; - Ok((route_id, blob)) + Ok(RouteBlob { route_id, blob }) } /// Import a private route blob as a remote private route. diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 2c1acbc3..ace5d53b 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -671,7 +671,7 @@ impl VeilidAPI { .unwrap_or_else(|_| crypto.best()); // Generate a keypair - let out = KeyPair::new(vcrypto.kind(), vcrypto.generate_keypair()).to_string(); + let out = vcrypto.generate_keypair().to_string(); Ok(out) } @@ -1575,7 +1575,7 @@ impl VeilidAPI { }; // Do a record create - let record = match rc.create_dht_record(schema, None, Some(csv.kind())).await { + let record = match rc.create_dht_record(csv.kind(), schema, None).await { Err(e) => return Ok(format!("Can't open DHT record: {}", e)), Ok(v) => v, }; diff --git a/veilid-core/src/veilid_api/routing_context.rs b/veilid-core/src/veilid_api/routing_context.rs index 951697e3..85032a71 100644 --- a/veilid-core/src/veilid_api/routing_context.rs +++ b/veilid-core/src/veilid_api/routing_context.rs @@ -323,24 +323,23 @@ impl RoutingContext { /// Creates a new DHT record /// /// The record is considered 'open' after the create operation succeeds. + /// * 'kind' - specify a cryptosystem kind to use /// * 'schema' - the schema to use when creating the DHT record /// * 'owner' - optionally specify an owner keypair to use. If you leave this as None then a random one will be generated. If specified, the crypto kind of the owner must match that of the `kind` parameter - /// * 'kind' - specify a cryptosystem kind to use. Normally you will leave this as None to choose the 'best' cryptosystem available. /// Returns the newly allocated DHT record's key if successful. /// /// Note: if you pass in an owner keypair this call is a deterministic! This means that if you try to create a new record for a given owner and schema that already exists it *will* fail. #[instrument(target = "veilid_api", level = "debug", fields(__VEILID_LOG_KEY = self.log_key()), ret, err)] pub async fn create_dht_record( &self, + kind: CryptoKind, schema: DHTSchema, owner: Option, - kind: Option, ) -> VeilidAPIResult { veilid_log!(self debug "RoutingContext::create_dht_record(self: {:?}, schema: {:?}, owner: {:?}, kind: {:?})", self, schema, owner, kind); schema.validate()?; - let kind = kind.unwrap_or(best_crypto_kind()); Crypto::validate_crypto_kind(kind)?; let storage_manager = self.api.core_context()?.storage_manager(); diff --git a/veilid-core/src/veilid_api/serialize_helpers/serialize_untyped_vld0.rs b/veilid-core/src/veilid_api/serialize_helpers/serialize_untyped_vld0.rs index fb507e2f..b70471be 100644 --- a/veilid-core/src/veilid_api/serialize_helpers/serialize_untyped_vld0.rs +++ b/veilid-core/src/veilid_api/serialize_helpers/serialize_untyped_vld0.rs @@ -2,7 +2,7 @@ macro_rules! untyped_vld0_serializers { ($rust_name:ident, $typed:ty, $bare:ty) => { paste::paste! { pub mod [< $rust_name _try_untyped_vld0 >] { - use crate::{Encodable, CRYPTO_KIND_VLD0}; + use crate::CRYPTO_KIND_VLD0; use core::str::FromStr; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/veilid-core/src/veilid_api/tests/test_types_dht.rs b/veilid-core/src/veilid_api/tests/test_types_dht.rs index 94b23e7a..12b1dffa 100644 --- a/veilid-core/src/veilid_api/tests/test_types_dht.rs +++ b/veilid-core/src/veilid_api/tests/test_types_dht.rs @@ -8,7 +8,7 @@ pub fn test_dhtrecorddescriptor() { let orig = DHTRecordDescriptor::new( fix_fake_record_key(), fix_fake_public_key(), - Some(fix_fake_bare_secret_key()), + Some(fix_fake_secret_key()), DHTSchema::dflt(4321).unwrap(), ); let copy = deserialize_json(&serialize_json(&orig)).unwrap(); diff --git a/veilid-core/src/veilid_api/types/aligned_u64.rs b/veilid-core/src/veilid_api/types/aligned_u64.rs index d4afd280..ed0f68a0 100644 --- a/veilid-core/src/veilid_api/types/aligned_u64.rs +++ b/veilid-core/src/veilid_api/types/aligned_u64.rs @@ -20,7 +20,11 @@ macro_rules! aligned_u64_type { Deserialize, JsonSchema, )] - #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(from_wasm_abi, into_wasm_abi) + )] #[repr(C, align(8))] #[serde(transparent)] #[must_use] 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 1c39d7a8..241c03b7 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 @@ -1,12 +1,8 @@ use super::*; /// DHT Record Descriptor +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] #[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr( - all(target_arch = "wasm32", target_os = "unknown"), - derive(Tsify), - tsify(from_wasm_abi, into_wasm_abi) -)] #[must_use] pub struct DHTRecordDescriptor { /// DHT Key = Hash(ownerKeyKind) of: [ ownerKeyValue, schema ] @@ -14,13 +10,11 @@ pub struct DHTRecordDescriptor { key: RecordKey, /// The public key of the owner #[schemars(with = "String")] - #[serde(with = "public_key_try_untyped_vld0")] owner: PublicKey, /// 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(all(target_arch = "wasm32", target_os = "unknown"), tsify(optional))] - owner_secret: Option, + owner_secret: Option, /// The schema in use associated with the key schema: DHTSchema, } @@ -29,9 +23,12 @@ impl DHTRecordDescriptor { pub(crate) fn new( key: RecordKey, owner: PublicKey, - owner_secret: Option, + owner_secret: Option, schema: DHTSchema, ) -> Self { + if let Some(owner_secret) = &owner_secret { + assert_eq!(owner_secret.kind(), owner.kind()); + } Self { key, owner, @@ -39,29 +36,64 @@ impl DHTRecordDescriptor { schema, } } - - pub fn key(&self) -> RecordKey { - self.key.clone() - } pub fn ref_key(&self) -> &RecordKey { &self.key } - pub fn owner(&self) -> PublicKey { - self.owner.clone() - } pub fn ref_owner(&self) -> &PublicKey { &self.owner } #[must_use] - pub fn owner_secret(&self) -> Option { - self.owner_secret.clone() - } - #[must_use] - pub fn ref_owner_secret(&self) -> Option<&BareSecretKey> { + pub fn ref_owner_secret(&self) -> Option<&SecretKey> { self.owner_secret.as_ref() } - - pub fn schema(&self) -> &DHTSchema { + pub fn ref_schema(&self) -> &DHTSchema { &self.schema } } + +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] +impl DHTRecordDescriptor { + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn key(&self) -> RecordKey { + self.key.clone() + } + + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn owner(&self) -> PublicKey { + self.owner.clone() + } + + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + #[must_use] + pub fn owner_secret(&self) -> Option { + self.owner_secret.clone() + } + + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn schema(&self) -> DHTSchema { + self.schema.clone() + } + + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + #[must_use] + pub fn owner_keypair(&self) -> Option { + self.owner_secret + .as_ref() + .map(|s| KeyPair::new_from_parts(self.owner.clone(), s.ref_value().clone())) + } +} diff --git a/veilid-core/src/veilid_api/types/dht/schema/mod.rs b/veilid-core/src/veilid_api/types/dht/schema/mod.rs index 4d251f7e..ef72ae1c 100644 --- a/veilid-core/src/veilid_api/types/dht/schema/mod.rs +++ b/veilid-core/src/veilid_api/types/dht/schema/mod.rs @@ -12,7 +12,7 @@ pub use smpl::*; #[cfg_attr( all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify), - tsify(from_wasm_abi) + tsify(from_wasm_abi, into_wasm_abi) )] #[must_use] pub enum DHTSchema { diff --git a/veilid-core/src/veilid_api/types/dht/set_dht_value_options.rs b/veilid-core/src/veilid_api/types/dht/set_dht_value_options.rs index 35794d53..ef7ca7a3 100644 --- a/veilid-core/src/veilid_api/types/dht/set_dht_value_options.rs +++ b/veilid-core/src/veilid_api/types/dht/set_dht_value_options.rs @@ -1,13 +1,10 @@ -use crate::{Deserialize, JsonSchema, KeyPair, Serialize}; - -#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] -use crate::Tsify; +use super::*; #[derive(Debug, JsonSchema, Serialize, Deserialize, PartialEq, Eq, Clone)] #[cfg_attr( all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify), - tsify(from_wasm_abi, into_wasm_abi) + tsify(into_wasm_abi) )] pub struct AllowOffline(pub bool); impl Default for AllowOffline { @@ -17,11 +14,6 @@ impl Default for AllowOffline { } #[derive(Debug, JsonSchema, Serialize, Deserialize, Clone)] -#[cfg_attr( - all(target_arch = "wasm32", target_os = "unknown"), - derive(Tsify), - tsify(from_wasm_abi, into_wasm_abi) -)] pub struct SetDHTValueOptions { #[schemars(with = "Option")] pub writer: Option, @@ -38,3 +30,34 @@ impl Default for SetDHTValueOptions { } } } + +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +pub mod ts { + use super::*; + + #[derive(Serialize, Deserialize, Clone, Tsify)] + #[tsify(from_wasm_abi, into_wasm_abi)] + pub struct SetDHTValueOptions { + #[tsify(type = "KeyPair", optional)] + #[serde(with = "serde_wasm_bindgen::preserve")] + pub writer: JsValue, + /// Defaults to true. If false, the value will not be written if the node is offline, + /// and a TryAgain error will be returned. + pub allow_offline: Option, + } +} + +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +impl TryFrom for SetDHTValueOptions { + type Error = VeilidAPIError; + + fn try_from(value: ts::SetDHTValueOptions) -> Result { + let writer = wasm_bindgen_derive::try_from_js_option::(value.writer) + .map_err(VeilidAPIError::generic)?; + let allow_offline = value.allow_offline.clone(); + Ok(SetDHTValueOptions { + writer, + allow_offline, + }) + } +} 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 1b4a1b8c..141f17ba 100644 --- a/veilid-core/src/veilid_api/types/dht/value_data.rs +++ b/veilid-core/src/veilid_api/types/dht/value_data.rs @@ -4,9 +4,9 @@ use veilid_api::VeilidAPIResult; #[derive(Clone, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, JsonSchema)] #[cfg_attr( all(target_arch = "wasm32", target_os = "unknown"), - derive(Tsify), - tsify(into_wasm_abi) + derive(wasm_bindgen_derive::TryFromJsValue) )] +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] #[must_use] pub struct ValueData { /// An increasing sequence number to time-order the DHT record changes @@ -20,8 +20,7 @@ pub struct ValueData { #[schemars(with = "String")] #[cfg_attr( all(target_arch = "wasm32", target_os = "unknown"), - serde(with = "serde_bytes"), - tsify(type = "Uint8Array") + serde(with = "serde_bytes") )] data: Vec, @@ -31,6 +30,9 @@ pub struct ValueData { writer: PublicKey, } +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +make_wasm_bindgen_stubs!(ValueData); + impl ValueData { pub const MAX_LEN: usize = 32768; @@ -55,15 +57,6 @@ impl ValueData { Ok(Self { seq, data, writer }) } - #[must_use] - pub fn seq(&self) -> ValueSeqNum { - self.seq - } - - pub fn writer(&self) -> PublicKey { - self.writer.clone() - } - pub fn ref_writer(&self) -> &PublicKey { &self.writer } @@ -72,18 +65,56 @@ impl ValueData { pub fn data(&self) -> &[u8] { &self.data } +} +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)] +impl ValueData { + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + #[must_use] + pub fn seq(&self) -> ValueSeqNum { + self.seq + } + + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] + pub fn writer(&self) -> PublicKey { + self.writer.clone() + } + + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] #[must_use] pub fn data_size(&self) -> usize { self.data.len() } + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + wasm_bindgen(getter) + )] #[must_use] pub fn total_size(&self) -> usize { mem::size_of::() + self.data.len() } } +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +#[wasm_bindgen] +impl ValueData { + #[wasm_bindgen(getter, js_name = data)] + #[must_use] + pub fn js_data(&self) -> Box<[u8]> { + self.data.clone().into_boxed_slice() + } +} + impl fmt::Debug for ValueData { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("ValueData") diff --git a/veilid-core/src/veilid_api/types/fourcc.rs b/veilid-core/src/veilid_api/types/fourcc.rs index 271f50bd..d2e7af25 100644 --- a/veilid-core/src/veilid_api/types/fourcc.rs +++ b/veilid-core/src/veilid_api/types/fourcc.rs @@ -1,89 +1,99 @@ #[macro_export] macro_rules! fourcc_type { ($name:ident) => { - /// A four-character code - #[derive( - Copy, - Default, - Clone, - Hash, - PartialOrd, - Ord, - PartialEq, - Eq, - Serialize, - Deserialize, - JsonSchema, - )] - #[serde(try_from = "String")] - #[serde(into = "String")] - #[must_use] - pub struct $name(pub [u8; 4]); + paste::paste! { + /// A four-character code + #[derive( + Copy, + Default, + Clone, + Hash, + PartialOrd, + Ord, + PartialEq, + Eq, + Serialize, + Deserialize, + JsonSchema, + )] + #[serde(try_from = "String", into = "String")] + #[must_use] + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi, type_suffix = "Inner") + )] + pub struct $name(#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"),tsify(type = "string"))] [u8; 4]); - cfg_if::cfg_if! { - if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { - #[wasm_bindgen(typescript_custom_section)] - const FOURCC_TYPE: &'static str = concat!(r#" -export type "#, stringify!($name), r#" = string; -"#); + impl $name { + pub const fn new(b: [u8; 4]) -> Self { + $name(b) + } + #[must_use] + pub fn bytes(&self) -> &[u8; 4] { + &self.0 + } } - } - impl From<[u8; 4]> for $name { - fn from(b: [u8; 4]) -> Self { - Self(b) - } - } + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + impl_opaque_newtype!($name, [< $name Inner >]); - impl From for $name { - fn from(u: u32) -> Self { - Self(u.to_be_bytes()) + impl From<[u8; 4]> for $name { + fn from(b: [u8; 4]) -> Self { + Self(b) + } } - } - impl From<$name> for u32 { - fn from(u: $name) -> Self { - u32::from_be_bytes(u.0) + impl From for $name { + fn from(u: u32) -> Self { + Self(u.to_be_bytes()) + } } - } - impl From<$name> for String { - fn from(u: $name) -> Self { - String::from_utf8_lossy(&u.0).to_string() + impl From<$name> for u32 { + fn from(u: $name) -> Self { + u32::from_be_bytes(u.0) + } } - } - impl TryFrom<&[u8]> for $name { - type Error = VeilidAPIError; - fn try_from(b: &[u8]) -> Result { - Ok(Self(b.try_into().map_err(VeilidAPIError::generic)?)) + impl From<$name> for String { + fn from(u: $name) -> Self { + String::from_utf8_lossy(&u.0).to_string() + } } - } - impl TryFrom for $name { - type Error = VeilidAPIError; - fn try_from(s: String) -> Result { - Self::from_str(s.as_str()) + impl TryFrom<&[u8]> for $name { + type Error = VeilidAPIError; + fn try_from(b: &[u8]) -> Result { + Ok(Self(b.try_into().map_err(VeilidAPIError::generic)?)) + } } - } - impl fmt::Display for $name { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", String::from_utf8_lossy(&self.0)) + impl TryFrom for $name { + type Error = VeilidAPIError; + fn try_from(s: String) -> Result { + Self::from_str(s.as_str()) + } } - } - impl fmt::Debug for $name { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", String::from_utf8_lossy(&self.0)) - } - } - impl FromStr for $name { - type Err = VeilidAPIError; - fn from_str(s: &str) -> Result { - Ok(Self( - s.as_bytes().try_into().map_err(VeilidAPIError::generic)?, - )) + impl fmt::Display for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", String::from_utf8_lossy(&self.0)) + } + } + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", String::from_utf8_lossy(&self.0)) + } + } + + impl FromStr for $name { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + Ok(Self( + s.as_bytes().try_into().map_err(VeilidAPIError::generic)?, + )) + } } } }; diff --git a/veilid-core/src/veilid_api/types/mod.rs b/veilid-core/src/veilid_api/types/mod.rs index 95fd85e5..8e723598 100644 --- a/veilid-core/src/veilid_api/types/mod.rs +++ b/veilid-core/src/veilid_api/types/mod.rs @@ -5,6 +5,7 @@ mod app_message_call; mod country_code; mod dht; mod fourcc; +mod route_blob; mod safety; mod stats; mod timestamp; @@ -13,6 +14,8 @@ mod timestamp_duration; mod tunnel; mod veilid_log; mod veilid_state; +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +mod wasm_helpers; use super::*; @@ -21,6 +24,7 @@ pub use app_message_call::*; #[cfg(feature = "geolocation")] pub use country_code::*; pub use dht::*; +pub use route_blob::*; pub use safety::*; pub use stats::*; pub use timestamp::*; diff --git a/veilid-core/src/veilid_api/types/route_blob.rs b/veilid-core/src/veilid_api/types/route_blob.rs new file mode 100644 index 00000000..03afa9c8 --- /dev/null +++ b/veilid-core/src/veilid_api/types/route_blob.rs @@ -0,0 +1,28 @@ +use super::*; + +#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(from_wasm_abi, into_wasm_abi) +)] +pub struct RouteBlob { + #[serde(with = "as_human_string")] + #[schemars(with = "String")] + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + tsify(type = "string") + )] + pub route_id: RouteId, + #[cfg_attr( + not(all(target_arch = "wasm32", target_os = "unknown")), + serde(with = "as_human_base64") + )] + #[schemars(with = "String")] + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + serde(with = "serde_bytes"), + tsify(type = "Uint8Array") + )] + pub blob: Vec, +} diff --git a/veilid-core/src/veilid_api/types/safety.rs b/veilid-core/src/veilid_api/types/safety.rs index 55d6939d..8fb51456 100644 --- a/veilid-core/src/veilid_api/types/safety.rs +++ b/veilid-core/src/veilid_api/types/safety.rs @@ -23,6 +23,61 @@ impl Default for Sequencing { } } +impl Sequencing { + #[must_use] + pub fn matches_ordering(&self, ordering: SequenceOrdering) -> bool { + match self { + Sequencing::NoPreference => true, + Sequencing::PreferOrdered => true, + Sequencing::EnsureOrdered => match ordering { + SequenceOrdering::Unordered => false, + SequenceOrdering::Ordered => true, + }, + } + } +} + +// Ordering here matters, >= is used to check strength of sequencing requirement +#[allow(clippy::derived_hash_with_manual_eq)] +#[derive(Debug, PartialOrd, Ord, EnumSetType, Hash, Serialize, Deserialize, JsonSchema)] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(from_wasm_abi, into_wasm_abi, namespace) +)] +#[must_use] +#[enumset(repr = "u8")] +pub enum SequenceOrdering { + Unordered = 0, + Ordered = 1, +} + +impl SequenceOrdering { + /// The sequencing requirement that guarantees this ordering + pub fn strict_sequencing(&self) -> Sequencing { + match self { + SequenceOrdering::Unordered => Sequencing::NoPreference, + SequenceOrdering::Ordered => Sequencing::EnsureOrdered, + } + } + /// The lowest sequencing requirement that matches this ordering + pub fn minimum_sequencing(&self) -> Sequencing { + match self { + SequenceOrdering::Unordered => Sequencing::NoPreference, + SequenceOrdering::Ordered => Sequencing::PreferOrdered, + } + } + /// The highest sequencing requirement that allows this ordering + pub fn maximum_sequencing(&self) -> Sequencing { + match self { + SequenceOrdering::Unordered => Sequencing::PreferOrdered, + SequenceOrdering::Ordered => Sequencing::EnsureOrdered, + } + } +} + +pub type SequenceOrderingSet = EnumSet; + // Ordering here matters, >= is used to check strength of stability requirement #[derive( Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema, diff --git a/veilid-core/src/veilid_api/types/wasm_helpers.rs b/veilid-core/src/veilid_api/types/wasm_helpers.rs new file mode 100644 index 00000000..64fd1552 --- /dev/null +++ b/veilid-core/src/veilid_api/types/wasm_helpers.rs @@ -0,0 +1,56 @@ +use super::*; + +#[wasm_bindgen(typescript_custom_section)] +const OPAQUE_NEW_TYPES: &'static str = r#" +declare const OpaqueNewTypeSymbol: unique symbol +declare class OpaqueNewType { + private [OpaqueNewTypeSymbol]: S +} +type Opaque = (T & OpaqueNewType) | OpaqueNewType +"#; + +#[macro_export] +macro_rules! impl_opaque_newtype { + ($name:ident, $base:ident) => { + paste::paste! { + #[wasm_bindgen(typescript_custom_section)] + const [< IMPL_OPAQUE_NEW_TYPE_ $name:upper >]: &'static str = concat!(r#" +declare const "#, stringify!($name), r#"Symbol: unique symbol +export type "#, stringify!($name), r#" = Opaque<"#, stringify!($base), r#", typeof "#, stringify!($name), r#"Symbol> +"#); + } + }; +} + +#[macro_export] +macro_rules! make_wasm_bindgen_stubs { + ($name:ident) => { + paste::paste! { + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(typescript_type = $name)] + pub type [< TypeStub $name >]; + } + + // #[derive( + // Default, + // Clone, + // Hash, + // PartialOrd, + // Ord, + // PartialEq, + // Eq, + // Serialize, + // Deserialize, + // )] + // #[must_use] + // #[cfg_attr( + // all(target_arch = "wasm32", target_os = "unknown"), + // derive(Tsify), + // tsify(into_wasm_abi, from_wasm_abi) + // )] + // pub struct [< StringStub $name >](String); + + } + }; +} diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 7af57007..ead7a1eb 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -28,7 +28,11 @@ cfg_if::cfg_if! { /// ``` /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigHTTPS { pub enabled: bool, @@ -60,7 +64,11 @@ impl Default for VeilidConfigHTTPS { /// ``` /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigHTTP { pub enabled: bool, @@ -88,7 +96,11 @@ impl Default for VeilidConfigHTTP { /// To be implemented... /// #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigApplication { pub https: VeilidConfigHTTPS, @@ -106,7 +118,11 @@ pub struct VeilidConfigApplication { /// ``` /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigUDP { pub enabled: bool, @@ -145,7 +161,11 @@ impl Default for VeilidConfigUDP { /// public_address: '' /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigTCP { pub connect: bool, @@ -189,7 +209,11 @@ impl Default for VeilidConfigTCP { /// url: 'ws://localhost:5150/ws' /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigWS { pub connect: bool, @@ -235,7 +259,11 @@ impl Default for VeilidConfigWS { /// url: '' /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigWSS { pub connect: bool, @@ -268,7 +296,11 @@ impl Default for VeilidConfigWSS { /// sort out which protocol is used for each peer connection. /// #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigProtocol { pub udp: VeilidConfigUDP, @@ -285,7 +317,11 @@ pub struct VeilidConfigProtocol { /// country_code_denylist: [] # only with `--features=geolocation` /// ``` #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] +#[cfg_attr( + target_arch = "wasm32", + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigPrivacy { pub require_inbound_relay: bool, @@ -302,7 +338,11 @@ pub struct VeilidConfigPrivacy { /// ``` #[cfg(feature = "virtual-network")] #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(target_arch = "wasm32", derive(Tsify))] +#[cfg_attr( + target_arch = "wasm32", + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigVirtualNetwork { pub enabled: bool, @@ -318,7 +358,11 @@ pub struct VeilidConfigVirtualNetwork { /// connection_initial_timeout_ms: 2000 /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigTLS { pub certificate_path: String, @@ -366,7 +410,11 @@ pub fn get_default_ssl_directory( /// If you change the count/fanout/timeout parameters, you may render your node inoperable /// for correct DHT operations. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigDHT { pub max_find_node_count: u32, @@ -451,7 +499,11 @@ impl Default for VeilidConfigDHT { /// Configure RPC. /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigRPC { pub concurrency: u32, @@ -482,15 +534,31 @@ impl Default for VeilidConfigRPC { /// Configure the network routing table. /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigRoutingTable { #[schemars(with = "Vec")] + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + tsify(type = "string[]") + )] pub public_keys: PublicKeyGroup, #[schemars(with = "Vec")] + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + tsify(type = "string[]") + )] pub secret_keys: SecretKeyGroup, pub bootstrap: Vec, #[schemars(with = "Vec")] + #[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + tsify(type = "string[]") + )] pub bootstrap_keys: Vec, pub limit_over_attached: u32, pub limit_fully_attached: u32, @@ -534,7 +602,11 @@ impl Default for VeilidConfigRoutingTable { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigNetwork { pub connection_initial_timeout_ms: u32, @@ -592,7 +664,11 @@ impl Default for VeilidConfigNetwork { } #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigTableStore { pub directory: String, @@ -626,7 +702,11 @@ fn get_default_store_path( } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigBlockStore { pub directory: String, @@ -643,7 +723,11 @@ impl Default for VeilidConfigBlockStore { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigProtectedStore { pub allow_insecure_fallback: bool, @@ -669,17 +753,21 @@ impl Default for VeilidConfigProtectedStore { } #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(into_wasm_abi, from_wasm_abi) +)] #[must_use] pub struct VeilidConfigCapabilities { pub disable: Vec, } #[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] #[cfg_attr( all(target_arch = "wasm32", target_os = "unknown"), - tsify(namespace, from_wasm_abi) + derive(Tsify), + tsify(namespace, into_wasm_abi, from_wasm_abi) )] #[must_use] pub enum VeilidConfigLogLevel { @@ -772,9 +860,9 @@ impl fmt::Display for VeilidConfigLogLevel { /// Top level of the Veilid configuration tree #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] #[cfg_attr( all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), tsify(into_wasm_abi, from_wasm_abi) )] #[must_use] diff --git a/veilid-flutter/example/integration_test/app_test.dart b/veilid-flutter/example/integration_test/app_test.dart index cfd04a43..619947d1 100644 --- a/veilid-flutter/example/integration_test/app_test.dart +++ b/veilid-flutter/example/integration_test/app_test.dart @@ -27,8 +27,8 @@ void main() { tearDownAll(fixture.tearDown); group('Crypto Tests', () { - test('best cryptosystem', testBestCryptoSystem); - test('get cryptosystem', testGetCryptoSystem); + test('list cryptosystems', testListCryptoSystems); + test('get cryptosystem', testGetCryptoSystems); test('get cryptosystem invalid', testGetCryptoSystemInvalid); test('hash and verify password', testHashAndVerifyPassword); test('sign and verify signature', testSignAndVerifySignature); diff --git a/veilid-flutter/example/integration_test/test_crypto.dart b/veilid-flutter/example/integration_test/test_crypto.dart index 9a285eef..8bcd44e1 100644 --- a/veilid-flutter/example/integration_test/test_crypto.dart +++ b/veilid-flutter/example/integration_test/test_crypto.dart @@ -3,93 +3,102 @@ import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:veilid/veilid.dart'; -Future testBestCryptoSystem() async { - final cs = await Veilid.instance.bestCryptoSystem(); - expect(await cs.defaultSaltLength(), equals(16)); +Future testListCryptoSystems() async { + final cryptoKinds = Veilid.instance.validCryptoKinds(); + expect(cryptoKinds, isNotEmpty); } -Future testGetCryptoSystem() async { +Future testGetCryptoSystems() async { final cs = await Veilid.instance.getCryptoSystem(cryptoKindVLD0); expect(await cs.defaultSaltLength(), equals(16)); } +const CryptoKind invalidCryptoKind = cryptoKindNONE + 1; + Future testGetCryptoSystemInvalid() async { - await expectLater(() async => Veilid.instance.getCryptoSystem(cryptoKindNONE), + await expectLater( + () async => Veilid.instance.getCryptoSystem(invalidCryptoKind), throwsA(isA())); } Future testHashAndVerifyPassword() async { - final cs = await Veilid.instance.bestCryptoSystem(); - final nonce = await cs.randomNonce(); - final salt = nonce.decode(); + for (final kind in Veilid.instance.validCryptoKinds()) { + final cs = await Veilid.instance.getCryptoSystem(kind); + final nonce = await cs.randomNonce(); + final salt = nonce.decode(); - // Password match - final phash = await cs.hashPassword(utf8.encode('abc123'), salt); - expect(await cs.verifyPassword(utf8.encode('abc123'), phash), isTrue); + // Password match + final phash = await cs.hashPassword(utf8.encode('abc123'), salt); + expect(await cs.verifyPassword(utf8.encode('abc123'), phash), isTrue); - // Password mismatch - await cs.hashPassword(utf8.encode('abc1234'), salt); - expect(await cs.verifyPassword(utf8.encode('abc1235'), phash), isFalse); + // Password mismatch + await cs.hashPassword(utf8.encode('abc1234'), salt); + expect(await cs.verifyPassword(utf8.encode('abc1235'), phash), isFalse); + } } Future testSignAndVerifySignature() async { - final cs = await Veilid.instance.bestCryptoSystem(); - final kp1 = await cs.generateKeyPair(); - final kp2 = await cs.generateKeyPair(); + for (final kind in Veilid.instance.validCryptoKinds()) { + final cs = await Veilid.instance.getCryptoSystem(kind); + final kp1 = await cs.generateKeyPair(); + final kp2 = await cs.generateKeyPair(); - // BareSignature match - final sig = await cs.sign(kp1.key, kp1.secret, utf8.encode('abc123')); - expect(await cs.verify(kp1.key, utf8.encode('abc123'), sig), isTrue); + // Signature match + final sig = await cs.sign(kp1.key, kp1.secret, utf8.encode('abc123')); + expect(await cs.verify(kp1.key, utf8.encode('abc123'), sig), isTrue); - // BareSignature mismatch - final sig2 = await cs.sign(kp1.key, kp1.secret, utf8.encode('abc1234')); - expect(await cs.verify(kp1.key, utf8.encode('abc1234'), sig2), isTrue); - expect(await cs.verify(kp1.key, utf8.encode('abc12345'), sig2), isFalse); - expect(await cs.verify(kp2.key, utf8.encode('abc1234'), sig2), isFalse); + // Signature mismatch + final sig2 = await cs.sign(kp1.key, kp1.secret, utf8.encode('abc1234')); + expect(await cs.verify(kp1.key, utf8.encode('abc1234'), sig2), isTrue); + expect(await cs.verify(kp1.key, utf8.encode('abc12345'), sig2), isFalse); + expect(await cs.verify(kp2.key, utf8.encode('abc1234'), sig2), isFalse); + } } Future testSignAndVerifySignatures() async { - final cs = await Veilid.instance.bestCryptoSystem(); - final kind = cs.kind(); - final kp1 = await cs.generateKeyPair(); + final kps = []; + for (final kind in Veilid.instance.validCryptoKinds()) { + final cs = await Veilid.instance.getCryptoSystem(kind); + final kp = await cs.generateKeyPair(); + kps.add(kp); + } - // BareSignature match - final sigs = await Veilid.instance.generateSignatures( - utf8.encode('abc123'), [KeyPair.fromBareKeyPair(kind, kp1)]); + // Signature match + final sigs = + await Veilid.instance.generateSignatures(utf8.encode('abc123'), kps); + final pks = kps.map((kp) => kp.key).toList(); expect( - await Veilid.instance.verifySignatures( - [PublicKey(kind: kind, value: kp1.key)], utf8.encode('abc123'), sigs), - equals([PublicKey(kind: kind, value: kp1.key)])); - // BareSignature mismatch + await Veilid.instance.verifySignatures(pks, utf8.encode('abc123'), sigs), + equals(pks)); + // Signature mismatch expect( - await Veilid.instance.verifySignatures( - [PublicKey(kind: kind, value: kp1.key)], - utf8.encode('abc1234'), - sigs), + await Veilid.instance.verifySignatures(pks, utf8.encode('abc1234'), sigs), isNull); } Future testGenerateSharedSecret() async { - final cs = await Veilid.instance.bestCryptoSystem(); + for (final kind in Veilid.instance.validCryptoKinds()) { + final cs = await Veilid.instance.getCryptoSystem(kind); - final kp1 = await cs.generateKeyPair(); - final kp2 = await cs.generateKeyPair(); - final kp3 = await cs.generateKeyPair(); + final kp1 = await cs.generateKeyPair(); + final kp2 = await cs.generateKeyPair(); + final kp3 = await cs.generateKeyPair(); - final ssA = - await cs.generateSharedSecret(kp1.key, kp2.secret, utf8.encode('abc123')); - final ssB = - await cs.generateSharedSecret(kp2.key, kp1.secret, utf8.encode('abc123')); + final ssA = await cs.generateSharedSecret( + kp1.key, kp2.secret, utf8.encode('abc123')); + final ssB = await cs.generateSharedSecret( + kp2.key, kp1.secret, utf8.encode('abc123')); - expect(ssA, equals(ssB)); + expect(ssA, equals(ssB)); - final ssC = await cs.generateSharedSecret( - kp2.key, kp1.secret, utf8.encode('abc1234')); + final ssC = await cs.generateSharedSecret( + kp2.key, kp1.secret, utf8.encode('abc1234')); - expect(ssA, isNot(equals(ssC))); + expect(ssA, isNot(equals(ssC))); - final ssD = - await cs.generateSharedSecret(kp3.key, kp1.secret, utf8.encode('abc123')); + final ssD = await cs.generateSharedSecret( + kp3.key, kp1.secret, utf8.encode('abc123')); - expect(ssA, isNot(equals(ssD))); + expect(ssA, isNot(equals(ssD))); + } } diff --git a/veilid-flutter/example/integration_test/test_dht.dart b/veilid-flutter/example/integration_test/test_dht.dart index 839661a8..e2d05a9e 100644 --- a/veilid-flutter/example/integration_test/test_dht.dart +++ b/veilid-flutter/example/integration_test/test_dht.dart @@ -50,9 +50,11 @@ Future testDeleteDHTRecordNonexistent() async { Future testCreateDeleteDHTRecordSimple() async { final rc = await Veilid.instance.routingContext(); try { - final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1)); - await rc.closeDHTRecord(rec.key); - await rc.deleteDHTRecord(rec.key); + for (final kind in Veilid.instance.validCryptoKinds()) { + final rec = await rc.createDHTRecord(kind, const DHTSchema.dflt(oCnt: 1)); + await rc.closeDHTRecord(rec.key); + await rc.deleteDHTRecord(rec.key); + } } finally { rc.close(); } @@ -61,8 +63,10 @@ Future testCreateDeleteDHTRecordSimple() async { Future testCreateDeleteDHTRecordNoClose() async { final rc = await Veilid.instance.routingContext(); try { - final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1)); - await rc.deleteDHTRecord(rec.key); + for (final kind in Veilid.instance.validCryptoKinds()) { + final rec = await rc.createDHTRecord(kind, const DHTSchema.dflt(oCnt: 1)); + await rc.deleteDHTRecord(rec.key); + } } finally { rc.close(); } @@ -71,9 +75,11 @@ Future testCreateDeleteDHTRecordNoClose() async { Future testGetDHTValueNonexistent() async { final rc = await Veilid.instance.routingContext(); try { - final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1)); - expect(await rc.getDHTValue(rec.key, 0), isNull); - await rc.deleteDHTRecord(rec.key); + for (final kind in Veilid.instance.validCryptoKinds()) { + final rec = await rc.createDHTRecord(kind, const DHTSchema.dflt(oCnt: 1)); + expect(await rc.getDHTValue(rec.key, 0), isNull); + await rc.deleteDHTRecord(rec.key); + } } finally { rc.close(); } @@ -82,21 +88,23 @@ Future testGetDHTValueNonexistent() async { Future testSetGetDHTValue() async { final rc = await Veilid.instance.routingContext(); try { - final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2)); - expect(await rc.setDHTValue(rec.key, 0, utf8.encode('BLAH BLAH BLAH')), - isNull); - final vd2 = await rc.getDHTValue(rec.key, 0); - expect(vd2, isNotNull); + for (final kind in Veilid.instance.validCryptoKinds()) { + final rec = await rc.createDHTRecord(kind, const DHTSchema.dflt(oCnt: 2)); + expect(await rc.setDHTValue(rec.key, 0, utf8.encode('BLAH BLAH BLAH')), + isNull); + final vd2 = await rc.getDHTValue(rec.key, 0); + expect(vd2, isNotNull); - final vd3 = await rc.getDHTValue(rec.key, 0, forceRefresh: true); - expect(vd3, isNotNull); + final vd3 = await rc.getDHTValue(rec.key, 0, forceRefresh: true); + expect(vd3, isNotNull); - final vd4 = await rc.getDHTValue(rec.key, 1); - expect(vd4, isNull); + final vd4 = await rc.getDHTValue(rec.key, 1); + expect(vd4, isNull); - expect(vd2, equals(vd3)); + expect(vd2, equals(vd3)); - await rc.deleteDHTRecord(rec.key); + await rc.deleteDHTRecord(rec.key); + } } finally { rc.close(); } @@ -105,26 +113,30 @@ Future testSetGetDHTValue() async { Future testSetGetDHTValueWithOwner() async { final rc = await Veilid.instance.routingContext(); try { - final cs = await Veilid.instance.bestCryptoSystem(); - final ownerKeyPair = - KeyPair.fromBareKeyPair(cs.kind(), await cs.generateKeyPair()); + for (final kind in Veilid.instance.validCryptoKinds()) { + final cs = await Veilid.instance.getCryptoSystem(kind); - final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2), - owner: ownerKeyPair); - expect(await rc.setDHTValue(rec.key, 0, utf8.encode('BLAH BLAH BLAH')), - isNull); - final vd2 = await rc.getDHTValue(rec.key, 0); - expect(vd2, isNotNull); + final ownerKeyPair = await cs.generateKeyPair(); - final vd3 = await rc.getDHTValue(rec.key, 0, forceRefresh: true); - expect(vd3, isNotNull); + final rec = await rc.createDHTRecord(kind, const DHTSchema.dflt(oCnt: 2), + owner: ownerKeyPair); + expect( + await rc.setDHTValue(rec.key, 0, utf8.encode('BLAH BLAH BLAH'), + options: const SetDHTValueOptions(allowOffline: false)), + isNull); + final vd2 = await rc.getDHTValue(rec.key, 0); + expect(vd2, isNotNull); - final vd4 = await rc.getDHTValue(rec.key, 1); - expect(vd4, isNull); + final vd3 = await rc.getDHTValue(rec.key, 0, forceRefresh: true); + expect(vd3, isNotNull); - expect(vd2, equals(vd3)); + final vd4 = await rc.getDHTValue(rec.key, 1); + expect(vd4, isNull); - await rc.deleteDHTRecord(rec.key); + expect(vd2, equals(vd3)); + + await rc.deleteDHTRecord(rec.key); + } } finally { rc.close(); } @@ -133,24 +145,23 @@ Future testSetGetDHTValueWithOwner() async { Future testCreateDHTRecordWithDeterministicKey() async { final rc = await Veilid.instance.routingContext(); try { - final bestCryptoSystem = await Veilid.instance.bestCryptoSystem(); - final bestCryptoKind = bestCryptoSystem.kind(); - final ownerKeyPair = await bestCryptoSystem.generateKeyPair(); - final owner = ownerKeyPair.key; - final secret = ownerKeyPair.secret; - const schema = DHTSchema.dflt(oCnt: 1); - final dhtRecordKey = await rc.getDHTRecordKey( - schema, PublicKey(kind: bestCryptoKind, value: owner)); - final dhtRecord = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1), - owner: KeyPair.fromBareKeyPair(bestCryptoKind, ownerKeyPair), - kind: bestCryptoKind); - expect(dhtRecord.key, equals(dhtRecordKey)); - expect( - dhtRecord.owner, equals(PublicKey(kind: bestCryptoKind, value: owner))); - expect(dhtRecord.ownerSecret, equals(secret)); - expect(dhtRecord.schema, equals(schema)); - await rc.closeDHTRecord(dhtRecord.key); - await rc.deleteDHTRecord(dhtRecord.key); + for (final kind in Veilid.instance.validCryptoKinds()) { + final cs = await Veilid.instance.getCryptoSystem(kind); + final ownerKeyPair = await cs.generateKeyPair(); + final owner = ownerKeyPair.key; + final secret = ownerKeyPair.secret; + const schema = DHTSchema.dflt(oCnt: 1); + final dhtRecordKey = await rc.getDHTRecordKey(schema, owner); + final dhtRecord = await rc.createDHTRecord( + kind, const DHTSchema.dflt(oCnt: 1), + owner: ownerKeyPair); + expect(dhtRecord.key, equals(dhtRecordKey)); + expect(dhtRecord.owner, equals(owner)); + expect(dhtRecord.ownerSecret, equals(secret)); + expect(dhtRecord.schema, equals(schema)); + await rc.closeDHTRecord(dhtRecord.key); + await rc.deleteDHTRecord(dhtRecord.key); + } } finally { rc.close(); } @@ -159,135 +170,138 @@ Future testCreateDHTRecordWithDeterministicKey() async { Future testOpenWriterDHTValue() async { final rc = await Veilid.instance.routingContext(); try { - var rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2)); - final key = rec.key; - final owner = rec.owner; - final secret = rec.ownerSecret!; + for (final kind in Veilid.instance.validCryptoKinds()) { + final cs = await Veilid.instance.getCryptoSystem(kind); - final cs = await Veilid.instance.getCryptoSystem(rec.key.kind); - expect(await cs.validateKeyPair(owner.value, secret), isTrue); - final otherKeyPair = - KeyPair.fromBareKeyPair(cs.kind(), await cs.generateKeyPair()); + var rec = await rc.createDHTRecord(kind, const DHTSchema.dflt(oCnt: 2)); + final key = rec.key; + final owner = rec.owner; + final secret = rec.ownerSecret!; - final va = utf8.encode('Qwertyuiop Asdfghjkl Zxcvbnm'); - final vb = utf8.encode('1234567890'); - final vc = utf8.encode(r'!@#$%^&*()'); + expect(await cs.validateKeyPair(owner, secret), isTrue); + final otherKeyPair = await cs.generateKeyPair(); - // Test subkey writes - expect(await rc.setDHTValue(key, 1, va), isNull); + final va = utf8.encode('Qwertyuiop Asdfghjkl Zxcvbnm'); + final vb = utf8.encode('1234567890'); + final vc = utf8.encode(r'!@#$%^&*()'); - var vdtemp = await rc.getDHTValue(key, 1); - expect(vdtemp, isNotNull); - expect(vdtemp!.data, equals(va)); - expect(vdtemp.seq, equals(0)); - expect(vdtemp.writer, equals(owner)); + // Test subkey writes + expect(await rc.setDHTValue(key, 1, va), isNull); - expect(await rc.getDHTValue(key, 0), isNull); + var vdtemp = await rc.getDHTValue(key, 1); + expect(vdtemp, isNotNull); + expect(vdtemp!.data, equals(va)); + expect(vdtemp.seq, equals(0)); + expect(vdtemp.writer, equals(owner)); - expect(await rc.setDHTValue(key, 0, vb), isNull); + expect(await rc.getDHTValue(key, 0), isNull); - expect( - await rc.getDHTValue(key, 0, forceRefresh: true), - equals(ValueData( - data: vb, - seq: 0, - writer: owner, - ))); + expect(await rc.setDHTValue(key, 0, vb), isNull); - expect( - await rc.getDHTValue(key, 1, forceRefresh: true), - equals(ValueData( - data: va, - seq: 0, - writer: owner, - ))); + expect( + await rc.getDHTValue(key, 0, forceRefresh: true), + equals(ValueData( + data: vb, + seq: 0, + writer: owner, + ))); - // Equal value should not trigger sequence number update - expect(await rc.setDHTValue(key, 1, va), isNull); + expect( + await rc.getDHTValue(key, 1, forceRefresh: true), + equals(ValueData( + data: va, + seq: 0, + writer: owner, + ))); - // Different value should trigger sequence number update - expect(await rc.setDHTValue(key, 1, vb), isNull); + // Equal value should not trigger sequence number update + expect(await rc.setDHTValue(key, 1, va), isNull); - await settle(rc, key, 0); - await settle(rc, key, 1); + // Different value should trigger sequence number update + expect(await rc.setDHTValue(key, 1, vb), isNull); - // Now that we initialized some subkeys - // and verified they stored correctly - // Delete things locally and reopen and see if we can write - // with the same writer key - // + await settle(rc, key, 0); + await settle(rc, key, 1); - await rc.closeDHTRecord(key); - await rc.deleteDHTRecord(key); + // Now that we initialized some subkeys + // and verified they stored correctly + // Delete things locally and reopen and see if we can write + // with the same writer key + // - rec = await rc.openDHTRecord(key, - writer: KeyPair.fromPublicAndBareSecret(owner, secret)); - expect(rec, isNotNull); - expect(rec.key, equals(key)); - expect(rec.owner, equals(owner)); - expect(rec.ownerSecret, equals(secret)); - expect(rec.schema, isA()); - expect(rec.schema.oCnt, equals(2)); + await rc.closeDHTRecord(key); + await rc.deleteDHTRecord(key); - // Verify subkey 1 can be set before it is get but newer is available online - vdtemp = await rc.setDHTValue(key, 1, vc); - expect(vdtemp, isNotNull); - expect(vdtemp!.data, equals(vb)); - expect(vdtemp.seq, equals(1)); - expect(vdtemp.writer, equals(owner)); + rec = await rc.openDHTRecord(key, + writer: KeyPair(key: owner, secret: secret)); + expect(rec, isNotNull); + expect(rec.key, equals(key)); + expect(rec.owner, equals(owner)); + expect(rec.ownerSecret, equals(secret)); + expect(rec.schema, isA()); + expect(rec.schema.oCnt, equals(2)); - // Verify subkey 1 can be set a second time - // and it updates because seq is newer - expect(await rc.setDHTValue(key, 1, vc), isNull); + // Verify subkey 1 can be set before it is get but newer is available online + vdtemp = await rc.setDHTValue(key, 1, vc); + expect(vdtemp, isNotNull); + expect(vdtemp!.data, equals(vb)); + expect(vdtemp.seq, equals(1)); + expect(vdtemp.writer, equals(owner)); - // Verify the network got the subkey update with a refresh check - vdtemp = await rc.getDHTValue(key, 1, forceRefresh: true); - expect(vdtemp, isNotNull); - expect(vdtemp!.data, equals(vc)); - expect(vdtemp.seq, equals(2)); - expect(vdtemp.writer, equals(owner)); + // Verify subkey 1 can be set a second time + // and it updates because seq is newer + expect(await rc.setDHTValue(key, 1, vc), isNull); - // Delete things locally and reopen and see if we can write - // with a different writer key (should fail) - await rc.closeDHTRecord(key); - await rc.deleteDHTRecord(key); + // Verify the network got the subkey update with a refresh check + vdtemp = await rc.getDHTValue(key, 1, forceRefresh: true); + expect(vdtemp, isNotNull); + expect(vdtemp!.data, equals(vc)); + expect(vdtemp.seq, equals(2)); + expect(vdtemp.writer, equals(owner)); - rec = await rc.openDHTRecord(key, writer: otherKeyPair); - expect(rec, isNotNull); - expect(rec.key, equals(key)); - expect(rec.owner, equals(owner)); - expect(rec.ownerSecret, isNull); - expect(rec.schema, isA()); - expect(rec.schema.oCnt, equals(2)); + // Delete things locally and reopen and see if we can write + // with a different writer key (should fail) + await rc.closeDHTRecord(key); + await rc.deleteDHTRecord(key); - // Verify subkey 1 can NOT be set because we have the wrong writer - await expectLater(() async => rc.setDHTValue(key, 1, va), - throwsA(isA())); + rec = await rc.openDHTRecord(key, writer: otherKeyPair); + expect(rec, isNotNull); + expect(rec.key, equals(key)); + expect(rec.owner, equals(owner)); + expect(rec.ownerSecret, isNull); + expect(rec.schema, isA()); + expect(rec.schema.oCnt, equals(2)); - // Verify subkey 0 can NOT be set because we have the wrong writer - await expectLater(() async => rc.setDHTValue(key, 0, va), - throwsA(isA())); + // Verify subkey 1 can NOT be set because we have the wrong writer + await expectLater(() async => rc.setDHTValue(key, 1, va), + throwsA(isA())); - // Verify subkey 0 can be set because override with the right writer - // Should have prior sequence number as its returned value because it - // exists online at seq 0 - vdtemp = await rc.setDHTValue(key, 0, va, - options: SetDHTValueOptions( - writer: KeyPair.fromPublicAndBareSecret(owner, secret))); - expect(vdtemp, isNotNull); - expect(vdtemp!.data, equals(vb)); - expect(vdtemp.seq, equals(0)); - expect(vdtemp.writer, equals(owner)); + // Verify subkey 0 can NOT be set because we have the wrong writer + await expectLater(() async => rc.setDHTValue(key, 0, va), + throwsA(isA())); - // Should update the second time to seq 1 - vdtemp = await rc.setDHTValue(key, 0, va, - options: SetDHTValueOptions( - writer: KeyPair.fromPublicAndBareSecret(owner, secret))); - expect(vdtemp, isNull); + // Verify subkey 0 can be set because override with the right writer + // Should have prior sequence number as its returned value because it + // exists online at seq 0 + vdtemp = await rc.setDHTValue(key, 0, va, + options: + SetDHTValueOptions(writer: KeyPair(key: owner, secret: secret))); + expect(vdtemp, isNotNull); + expect(vdtemp!.data, equals(vb)); + expect(vdtemp.seq, equals(0)); + expect(vdtemp.writer, equals(owner)); - // Clean up - await rc.closeDHTRecord(key); - await rc.deleteDHTRecord(key); + // Should update the second time to seq 1 + vdtemp = await rc.setDHTValue(key, 0, va, + options: SetDHTValueOptions( + writer: KeyPair(key: owner, secret: secret), + allowOffline: false)); + expect(vdtemp, isNull); + + // Clean up + await rc.closeDHTRecord(key); + await rc.deleteDHTRecord(key); + } } finally { rc.close(); } @@ -351,128 +365,133 @@ Future testWatchDHTValues(Stream updateStream) async { final rcSet = await Veilid.instance.routingContext(); final rcWatch = await Veilid.instance.safeRoutingContext(); try { - // Make a DHT record - var rec = await rcWatch.createDHTRecord(const DHTSchema.dflt(oCnt: 10)); + for (final kind in Veilid.instance.validCryptoKinds()) { + // Make a DHT record + var rec = + await rcWatch.createDHTRecord(kind, const DHTSchema.dflt(oCnt: 10)); - // Set some subkey we care about - expect( - await rcWatch.setDHTValue(rec.key, 3, utf8.encode('BLAH BLAH BLAH')), - isNull); - - // Wait for set to settle - await settle(rcWatch, rec.key, 3); - - // Make a watch on that subkey - expect(await rcWatch.watchDHTValues(rec.key), - isNot(equals(Timestamp.zero()))); - - // Reopen without closing to change routing context and not lose watch - rec = await rcSet.openDHTRecord(rec.key, writer: rec.ownerKeyPair()); - - // Now we should NOT get an update because the update - // is the same as our local copy - final update1 = await waitForValueChange( - valueChangeQueue.stream, const Duration(seconds: 10), () async { - // Now set the subkey and trigger an update - expect(await rcSet.setDHTValue(rec.key, 3, utf8.encode('BLAH BLAH')), + // Set some subkey we care about + expect( + await rcWatch.setDHTValue( + rec.key, 3, utf8.encode('BLAH BLAH BLAH')), isNull); // Wait for set to settle - await settle(rcSet, rec.key, 3); - }); - if (update1 != null) { - fail('should not have a change'); - } + await settle(rcWatch, rec.key, 3); - // Wait for the update - final update2 = await waitForValueChange( - valueChangeQueue.stream, const Duration(seconds: 10), () async { - // Now set a subkey and trigger an update + // Make a watch on that subkey + expect(await rcWatch.watchDHTValues(rec.key), + isNot(equals(Timestamp.zero()))); + + // Reopen without closing to change routing context and not lose watch + rec = await rcSet.openDHTRecord(rec.key, writer: rec.ownerKeyPair); + + // Now we should NOT get an update because the update + // is the same as our local copy + final update1 = await waitForValueChange( + valueChangeQueue.stream, const Duration(seconds: 10), () async { + // Now set the subkey and trigger an update + expect(await rcSet.setDHTValue(rec.key, 3, utf8.encode('BLAH BLAH')), + isNull); + + // Wait for set to settle + await settle(rcSet, rec.key, 3); + }); + if (update1 != null) { + fail('should not have a change'); + } + + // Wait for the update + final update2 = await waitForValueChange( + valueChangeQueue.stream, const Duration(seconds: 10), () async { + // Now set a subkey and trigger an update + expect( + await rcSet.setDHTValue(rec.key, 3, utf8.encode('BLAH')), isNull); + + await settle(rcSet, rec.key, 3); + }); + if (update2 == null) { + fail('should have a change'); + } + + // Verify the update + expect(update2.key, equals(rec.key)); + expect(update2.count, equals(0xFFFFFFFD)); + expect(update2.subkeys, equals([ValueSubkeyRange.single(3)])); + expect(update2.value, isNull); + + // Reopen without closing to change routing context and not lose watch + rec = await rcWatch.openDHTRecord(rec.key, writer: rec.ownerKeyPair); + + // Cancel some subkeys we don't care about expect( - await rcSet.setDHTValue(rec.key, 3, utf8.encode('BLAH')), isNull); + await rcWatch.cancelDHTWatch(rec.key, + subkeys: [ValueSubkeyRange.make(0, 2)]), + isTrue); - await settle(rcSet, rec.key, 3); - }); - if (update2 == null) { - fail('should have a change'); - } + // Reopen without closing to change routing context and not lose watch + rec = await rcSet.openDHTRecord(rec.key, writer: rec.ownerKeyPair); - // Verify the update - expect(update2.key, equals(rec.key)); - expect(update2.count, equals(0xFFFFFFFD)); - expect(update2.subkeys, equals([ValueSubkeyRange.single(3)])); - expect(update2.value, isNull); + // Wait for the update + final update3 = await waitForValueChange( + valueChangeQueue.stream, const Duration(seconds: 10), () async { + // Now set multiple subkeys and trigger an update on one of them + expect( + await [ + rcSet.setDHTValue(rec.key, 3, utf8.encode('BLART')), + rcSet.setDHTValue(rec.key, 1, utf8.encode('BZORT BZORT')) + ].wait, + equals([null, null])); - // Reopen without closing to change routing context and not lose watch - rec = await rcWatch.openDHTRecord(rec.key, writer: rec.ownerKeyPair()); + await settle(rcSet, rec.key, 3); + await settle(rcSet, rec.key, 1); + }); + if (update3 == null) { + fail('should have a change'); + } - // Cancel some subkeys we don't care about - expect( - await rcWatch - .cancelDHTWatch(rec.key, subkeys: [ValueSubkeyRange.make(0, 2)]), - isTrue); + // Verify the update came back but we don't get a new value because the + // sequence number is the same + expect(update3.key, equals(rec.key)); + expect(update3.count, equals(0xFFFFFFFC)); + expect(update3.subkeys, equals([ValueSubkeyRange.single(3)])); + expect(update3.value, isNull); - // Reopen without closing to change routing context and not lose watch - rec = await rcSet.openDHTRecord(rec.key, writer: rec.ownerKeyPair()); + // Reopen without closing to change routing context and not lose watch + rec = await rcWatch.openDHTRecord(rec.key, writer: rec.ownerKeyPair); - // Wait for the update - final update3 = await waitForValueChange( - valueChangeQueue.stream, const Duration(seconds: 10), () async { - // Now set multiple subkeys and trigger an update on one of them + // Now cancel the update expect( - await [ - rcSet.setDHTValue(rec.key, 3, utf8.encode('BLART')), - rcSet.setDHTValue(rec.key, 1, utf8.encode('BZORT BZORT')) - ].wait, - equals([null, null])); + await rcWatch.cancelDHTWatch(rec.key, + subkeys: [ValueSubkeyRange.make(3, 9)]), + isFalse); - await settle(rcSet, rec.key, 3); - await settle(rcSet, rec.key, 1); - }); - if (update3 == null) { - fail('should have a change'); + // Reopen without closing to change routing context and not lose watch + rec = await rcSet.openDHTRecord(rec.key, writer: rec.ownerKeyPair); + + // Wait for the update + final update4 = await waitForValueChange( + valueChangeQueue.stream, const Duration(seconds: 10), () async { + // Now set multiple subkeys that should not trigger an update + expect( + await [ + rcSet.setDHTValue( + rec.key, 3, utf8.encode('BLAH BLAH BLAH BLAH')), + rcSet.setDHTValue(rec.key, 5, utf8.encode('BZORT BZORT BZORT')) + ].wait, + equals([null, null])); + + await settle(rcSet, rec.key, 3); + await settle(rcSet, rec.key, 5); + }); + if (update4 != null) { + fail('should not have a change'); + } + + // Clean up + await rcSet.closeDHTRecord(rec.key); + await rcSet.deleteDHTRecord(rec.key); } - - // Verify the update came back but we don't get a new value because the - // sequence number is the same - expect(update3.key, equals(rec.key)); - expect(update3.count, equals(0xFFFFFFFC)); - expect(update3.subkeys, equals([ValueSubkeyRange.single(3)])); - expect(update3.value, isNull); - - // Reopen without closing to change routing context and not lose watch - rec = await rcWatch.openDHTRecord(rec.key, writer: rec.ownerKeyPair()); - - // Now cancel the update - expect( - await rcWatch - .cancelDHTWatch(rec.key, subkeys: [ValueSubkeyRange.make(3, 9)]), - isFalse); - - // Reopen without closing to change routing context and not lose watch - rec = await rcSet.openDHTRecord(rec.key, writer: rec.ownerKeyPair()); - - // Wait for the update - final update4 = await waitForValueChange( - valueChangeQueue.stream, const Duration(seconds: 10), () async { - // Now set multiple subkeys that should not trigger an update - expect( - await [ - rcSet.setDHTValue(rec.key, 3, utf8.encode('BLAH BLAH BLAH BLAH')), - rcSet.setDHTValue(rec.key, 5, utf8.encode('BZORT BZORT BZORT')) - ].wait, - equals([null, null])); - - await settle(rcSet, rec.key, 3); - await settle(rcSet, rec.key, 5); - }); - if (update4 != null) { - fail('should not have a change'); - } - - // Clean up - await rcSet.closeDHTRecord(rec.key); - await rcSet.deleteDHTRecord(rec.key); } finally { rcWatch.close(); rcSet.close(); @@ -486,26 +505,28 @@ Future testWatchDHTValues(Stream updateStream) async { Future testInspectDHTRecord() async { final rc = await Veilid.instance.routingContext(); try { - final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2)); + for (final kind in Veilid.instance.validCryptoKinds()) { + final rec = await rc.createDHTRecord(kind, const DHTSchema.dflt(oCnt: 2)); - expect(await rc.setDHTValue(rec.key, 0, utf8.encode('BLAH BLAH BLAH')), - isNull); + expect( + await rc.setDHTValue(rec.key, 0, utf8.encode('BLAH BLAH BLAH'), + options: const SetDHTValueOptions(allowOffline: false)), + isNull); - await settle(rc, rec.key, 0); + final rr = await rc.inspectDHTRecord(rec.key); + expect(rr.subkeys, equals([ValueSubkeyRange.make(0, 1)])); + expect(rr.localSeqs, equals([0, null])); + expect(rr.networkSeqs, equals([null, null])); - final rr = await rc.inspectDHTRecord(rec.key); - expect(rr.subkeys, equals([ValueSubkeyRange.make(0, 1)])); - expect(rr.localSeqs, equals([0, null])); - expect(rr.networkSeqs, equals([null, null])); + final rr2 = + await rc.inspectDHTRecord(rec.key, scope: DHTReportScope.syncGet); + expect(rr2.subkeys, equals([ValueSubkeyRange.make(0, 1)])); + expect(rr2.localSeqs, equals([0, null])); + expect(rr2.networkSeqs, equals([0, null])); - final rr2 = - await rc.inspectDHTRecord(rec.key, scope: DHTReportScope.syncGet); - expect(rr2.subkeys, equals([ValueSubkeyRange.make(0, 1)])); - expect(rr2.localSeqs, equals([0, null])); - expect(rr2.networkSeqs, equals([0, null])); - - await rc.closeDHTRecord(rec.key); - await rc.deleteDHTRecord(rec.key); + await rc.closeDHTRecord(rec.key); + await rc.deleteDHTRecord(rec.key); + } } finally { rc.close(); } diff --git a/veilid-flutter/example/integration_test/test_routing_context.dart b/veilid-flutter/example/integration_test/test_routing_context.dart index 82bf4b85..acac700a 100644 --- a/veilid-flutter/example/integration_test/test_routing_context.dart +++ b/veilid-flutter/example/integration_test/test_routing_context.dart @@ -158,7 +158,8 @@ Future testAppMessageLoopbackBigPackets( final sentMessages = {}; final random = Random.secure(); - final cs = await Veilid.instance.bestCryptoSystem(); + final cs = await Veilid.instance + .getCryptoSystem(Veilid.instance.validCryptoKinds().first); try { await Veilid.instance.debug('purge routes'); @@ -223,7 +224,8 @@ Future testAppCallLoopbackBigPackets( final sentMessages = {}; final random = Random.secure(); - final cs = await Veilid.instance.bestCryptoSystem(); + final cs = await Veilid.instance + .getCryptoSystem(Veilid.instance.validCryptoKinds().first); try { await Veilid.instance.debug('purge routes'); diff --git a/veilid-flutter/lib/routing_context.dart b/veilid-flutter/lib/routing_context.dart index b07df726..621fdce2 100644 --- a/veilid-flutter/lib/routing_context.dart +++ b/veilid-flutter/lib/routing_context.dart @@ -23,12 +23,12 @@ extension ValidateDFLT on DHTSchemaDFLT { return true; } - int subkeyCount() => oCnt; + int get subkeyCount => oCnt; } extension ValidateSMPL on DHTSchemaSMPL { bool validate() { - final totalsv = subkeyCount(); + final totalsv = subkeyCount; if (totalsv > 65535) { return false; } @@ -38,7 +38,7 @@ extension ValidateSMPL on DHTSchemaSMPL { return true; } - int subkeyCount() => members.fold(oCnt, (acc, v) => acc + v.mCnt); + int get subkeyCount => members.fold(oCnt, (acc, v) => acc + v.mCnt); } extension Validate on DHTSchema { @@ -51,11 +51,11 @@ extension Validate on DHTSchema { throw TypeError(); } - int subkeyCount() { + int get subkeyCount { if (this is DHTSchemaDFLT) { - return (this as DHTSchemaDFLT).subkeyCount(); + return (this as DHTSchemaDFLT).subkeyCount; } else if (this is DHTSchemaSMPL) { - return (this as DHTSchemaSMPL).subkeyCount(); + return (this as DHTSchemaSMPL).subkeyCount; } throw TypeError(); } @@ -101,32 +101,32 @@ sealed class DHTRecordDescriptor with _$DHTRecordDescriptor { required RecordKey key, required PublicKey owner, required DHTSchema schema, - BareSecretKey? ownerSecret, + SecretKey? ownerSecret, }) = _DHTRecordDescriptor; factory DHTRecordDescriptor.fromJson(dynamic json) => _$DHTRecordDescriptorFromJson(json as Map); } extension DHTRecordDescriptorExt on DHTRecordDescriptor { - BareKeyPair? ownerBareKeyPair() { + BareKeyPair? get ownerBareKeyPair { if (ownerSecret == null) { return null; } - return BareKeyPair(key: owner.value, secret: ownerSecret!); + return BareKeyPair(key: owner.value, secret: ownerSecret!.value); } - SecretKey? ownerSecretKey() { + BareSecretKey? get ownerBareSecretKey { if (ownerSecret == null) { return null; } - return SecretKey(kind: owner.kind, value: ownerSecret!); + return ownerSecret!.value; } - KeyPair? ownerKeyPair() { + KeyPair? get ownerKeyPair { if (ownerSecret == null) { return null; } - return KeyPair(kind: owner.kind, key: owner.value, secret: ownerSecret!); + return KeyPair(key: owner, secret: ownerSecret!); } } @@ -291,8 +291,9 @@ class TargetRouteId extends Equatable implements Target { @freezed sealed class RouteBlob with _$RouteBlob { const factory RouteBlob( - {required RouteId routeId, - @Uint8ListJsonConverter() required Uint8List blob}) = _RouteBlob; + {required RouteId routeId, + @Uint8ListJsonConverter.jsIsArray() required Uint8List blob}) = + _RouteBlob; factory RouteBlob.fromJson(dynamic json) => _$RouteBlobFromJson(json as Map); } @@ -361,8 +362,8 @@ abstract class VeilidRoutingContext { Future appMessage(Target target, Uint8List message); // DHT Operations - Future createDHTRecord(DHTSchema schema, - {KeyPair? owner, CryptoKind kind = 0}); + Future createDHTRecord(CryptoKind kind, DHTSchema schema, + {KeyPair? owner}); Future openDHTRecord(RecordKey key, {KeyPair? writer}); Future closeDHTRecord(RecordKey key); Future deleteDHTRecord(RecordKey key); diff --git a/veilid-flutter/lib/routing_context.freezed.dart b/veilid-flutter/lib/routing_context.freezed.dart index 40bf1bf8..b1c1ba27 100644 --- a/veilid-flutter/lib/routing_context.freezed.dart +++ b/veilid-flutter/lib/routing_context.freezed.dart @@ -434,7 +434,7 @@ mixin _$DHTRecordDescriptor { RecordKey get key; PublicKey get owner; DHTSchema get schema; - BareSecretKey? get ownerSecret; + SecretKey? get ownerSecret; /// Create a copy of DHTRecordDescriptor /// with the given fields replaced by the non-null parameter values. @@ -479,7 +479,7 @@ abstract mixin class $DHTRecordDescriptorCopyWith<$Res> { {RecordKey key, PublicKey owner, DHTSchema schema, - BareSecretKey? ownerSecret}); + SecretKey? ownerSecret}); $DHTSchemaCopyWith<$Res> get schema; } @@ -518,7 +518,7 @@ class _$DHTRecordDescriptorCopyWithImpl<$Res> ownerSecret: freezed == ownerSecret ? _self.ownerSecret : ownerSecret // ignore: cast_nullable_to_non_nullable - as BareSecretKey?, + as SecretKey?, )); } @@ -551,7 +551,7 @@ class _DHTRecordDescriptor implements DHTRecordDescriptor { @override final DHTSchema schema; @override - final BareSecretKey? ownerSecret; + final SecretKey? ownerSecret; /// Create a copy of DHTRecordDescriptor /// with the given fields replaced by the non-null parameter values. @@ -603,7 +603,7 @@ abstract mixin class _$DHTRecordDescriptorCopyWith<$Res> {RecordKey key, PublicKey owner, DHTSchema schema, - BareSecretKey? ownerSecret}); + SecretKey? ownerSecret}); @override $DHTSchemaCopyWith<$Res> get schema; @@ -643,7 +643,7 @@ class __$DHTRecordDescriptorCopyWithImpl<$Res> ownerSecret: freezed == ownerSecret ? _self.ownerSecret : ownerSecret // ignore: cast_nullable_to_non_nullable - as BareSecretKey?, + as SecretKey?, )); } @@ -1053,7 +1053,7 @@ class __$SafetySpecCopyWithImpl<$Res> implements _$SafetySpecCopyWith<$Res> { /// @nodoc mixin _$RouteBlob { RouteId get routeId; - @Uint8ListJsonConverter() + @Uint8ListJsonConverter.jsIsArray() Uint8List get blob; /// Create a copy of RouteBlob @@ -1091,7 +1091,8 @@ abstract mixin class $RouteBlobCopyWith<$Res> { factory $RouteBlobCopyWith(RouteBlob value, $Res Function(RouteBlob) _then) = _$RouteBlobCopyWithImpl; @useResult - $Res call({RouteId routeId, @Uint8ListJsonConverter() Uint8List blob}); + $Res call( + {RouteId routeId, @Uint8ListJsonConverter.jsIsArray() Uint8List blob}); } /// @nodoc @@ -1126,14 +1127,15 @@ class _$RouteBlobCopyWithImpl<$Res> implements $RouteBlobCopyWith<$Res> { @JsonSerializable() class _RouteBlob implements RouteBlob { const _RouteBlob( - {required this.routeId, @Uint8ListJsonConverter() required this.blob}); + {required this.routeId, + @Uint8ListJsonConverter.jsIsArray() required this.blob}); factory _RouteBlob.fromJson(Map json) => _$RouteBlobFromJson(json); @override final RouteId routeId; @override - @Uint8ListJsonConverter() + @Uint8ListJsonConverter.jsIsArray() final Uint8List blob; /// Create a copy of RouteBlob @@ -1179,7 +1181,8 @@ abstract mixin class _$RouteBlobCopyWith<$Res> __$RouteBlobCopyWithImpl; @override @useResult - $Res call({RouteId routeId, @Uint8ListJsonConverter() Uint8List blob}); + $Res call( + {RouteId routeId, @Uint8ListJsonConverter.jsIsArray() Uint8List blob}); } /// @nodoc diff --git a/veilid-flutter/lib/routing_context.g.dart b/veilid-flutter/lib/routing_context.g.dart index d6196750..cca55707 100644 --- a/veilid-flutter/lib/routing_context.g.dart +++ b/veilid-flutter/lib/routing_context.g.dart @@ -53,7 +53,7 @@ _DHTRecordDescriptor _$DHTRecordDescriptorFromJson(Map json) => schema: DHTSchema.fromJson(json['schema']), ownerSecret: json['owner_secret'] == null ? null - : BareSecretKey.fromJson(json['owner_secret']), + : Typed.fromJson(json['owner_secret']), ); Map _$DHTRecordDescriptorToJson( @@ -97,13 +97,13 @@ Map _$SafetySpecToJson(_SafetySpec instance) => _RouteBlob _$RouteBlobFromJson(Map json) => _RouteBlob( routeId: Typed.fromJson(json['route_id']), - blob: const Uint8ListJsonConverter().fromJson(json['blob']), + blob: const Uint8ListJsonConverter.jsIsArray().fromJson(json['blob']), ); Map _$RouteBlobToJson(_RouteBlob instance) => { 'route_id': instance.routeId.toJson(), - 'blob': const Uint8ListJsonConverter().toJson(instance.blob), + 'blob': const Uint8ListJsonConverter.jsIsArray().toJson(instance.blob), }; _DHTRecordReport _$DHTRecordReportFromJson(Map json) => diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index c16a3c25..56f2f4f2 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -180,7 +180,6 @@ abstract class Veilid { // Crypto List validCryptoKinds(); Future getCryptoSystem(CryptoKind kind); - Future bestCryptoSystem(); Future?> verifySignatures( List publicKeys, Uint8List data, List signatures); Future> generateSignatures( diff --git a/veilid-flutter/lib/veilid_config.dart b/veilid-flutter/lib/veilid_config.dart index 5e78b873..ebda6370 100644 --- a/veilid-flutter/lib/veilid_config.dart +++ b/veilid-flutter/lib/veilid_config.dart @@ -344,8 +344,8 @@ sealed class VeilidConfigRPC with _$VeilidConfigRPC { @freezed sealed class VeilidConfigRoutingTable with _$VeilidConfigRoutingTable { const factory VeilidConfigRoutingTable({ - required List nodeId, - required List nodeIdSecret, + required List publicKeys, + required List secretKeys, required List bootstrap, required List bootstrapKeys, required int limitOverAttached, diff --git a/veilid-flutter/lib/veilid_config.freezed.dart b/veilid-flutter/lib/veilid_config.freezed.dart index 885a60c7..7011c503 100644 --- a/veilid-flutter/lib/veilid_config.freezed.dart +++ b/veilid-flutter/lib/veilid_config.freezed.dart @@ -5680,8 +5680,8 @@ class __$VeilidConfigRPCCopyWithImpl<$Res> /// @nodoc mixin _$VeilidConfigRoutingTable implements DiagnosticableTreeMixin { - List get nodeId; - List get nodeIdSecret; + List get publicKeys; + List get secretKeys; List get bootstrap; List get bootstrapKeys; int get limitOverAttached; @@ -5705,8 +5705,8 @@ mixin _$VeilidConfigRoutingTable implements DiagnosticableTreeMixin { void debugFillProperties(DiagnosticPropertiesBuilder properties) { properties ..add(DiagnosticsProperty('type', 'VeilidConfigRoutingTable')) - ..add(DiagnosticsProperty('nodeId', nodeId)) - ..add(DiagnosticsProperty('nodeIdSecret', nodeIdSecret)) + ..add(DiagnosticsProperty('publicKeys', publicKeys)) + ..add(DiagnosticsProperty('secretKeys', secretKeys)) ..add(DiagnosticsProperty('bootstrap', bootstrap)) ..add(DiagnosticsProperty('bootstrapKeys', bootstrapKeys)) ..add(DiagnosticsProperty('limitOverAttached', limitOverAttached)) @@ -5721,9 +5721,10 @@ mixin _$VeilidConfigRoutingTable implements DiagnosticableTreeMixin { return identical(this, other) || (other.runtimeType == runtimeType && other is VeilidConfigRoutingTable && - const DeepCollectionEquality().equals(other.nodeId, nodeId) && const DeepCollectionEquality() - .equals(other.nodeIdSecret, nodeIdSecret) && + .equals(other.publicKeys, publicKeys) && + const DeepCollectionEquality() + .equals(other.secretKeys, secretKeys) && const DeepCollectionEquality().equals(other.bootstrap, bootstrap) && const DeepCollectionEquality() .equals(other.bootstrapKeys, bootstrapKeys) && @@ -5743,8 +5744,8 @@ mixin _$VeilidConfigRoutingTable implements DiagnosticableTreeMixin { @override int get hashCode => Object.hash( runtimeType, - const DeepCollectionEquality().hash(nodeId), - const DeepCollectionEquality().hash(nodeIdSecret), + const DeepCollectionEquality().hash(publicKeys), + const DeepCollectionEquality().hash(secretKeys), const DeepCollectionEquality().hash(bootstrap), const DeepCollectionEquality().hash(bootstrapKeys), limitOverAttached, @@ -5755,7 +5756,7 @@ mixin _$VeilidConfigRoutingTable implements DiagnosticableTreeMixin { @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'VeilidConfigRoutingTable(nodeId: $nodeId, nodeIdSecret: $nodeIdSecret, bootstrap: $bootstrap, bootstrapKeys: $bootstrapKeys, limitOverAttached: $limitOverAttached, limitFullyAttached: $limitFullyAttached, limitAttachedStrong: $limitAttachedStrong, limitAttachedGood: $limitAttachedGood, limitAttachedWeak: $limitAttachedWeak)'; + return 'VeilidConfigRoutingTable(publicKeys: $publicKeys, secretKeys: $secretKeys, bootstrap: $bootstrap, bootstrapKeys: $bootstrapKeys, limitOverAttached: $limitOverAttached, limitFullyAttached: $limitFullyAttached, limitAttachedStrong: $limitAttachedStrong, limitAttachedGood: $limitAttachedGood, limitAttachedWeak: $limitAttachedWeak)'; } } @@ -5766,8 +5767,8 @@ abstract mixin class $VeilidConfigRoutingTableCopyWith<$Res> { _$VeilidConfigRoutingTableCopyWithImpl; @useResult $Res call( - {List nodeId, - List nodeIdSecret, + {List publicKeys, + List secretKeys, List bootstrap, List bootstrapKeys, int limitOverAttached, @@ -5790,8 +5791,8 @@ class _$VeilidConfigRoutingTableCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? nodeId = null, - Object? nodeIdSecret = null, + Object? publicKeys = null, + Object? secretKeys = null, Object? bootstrap = null, Object? bootstrapKeys = null, Object? limitOverAttached = null, @@ -5801,13 +5802,13 @@ class _$VeilidConfigRoutingTableCopyWithImpl<$Res> Object? limitAttachedWeak = null, }) { return _then(_self.copyWith( - nodeId: null == nodeId - ? _self.nodeId - : nodeId // ignore: cast_nullable_to_non_nullable + publicKeys: null == publicKeys + ? _self.publicKeys + : publicKeys // ignore: cast_nullable_to_non_nullable as List, - nodeIdSecret: null == nodeIdSecret - ? _self.nodeIdSecret - : nodeIdSecret // ignore: cast_nullable_to_non_nullable + secretKeys: null == secretKeys + ? _self.secretKeys + : secretKeys // ignore: cast_nullable_to_non_nullable as List, bootstrap: null == bootstrap ? _self.bootstrap @@ -5847,8 +5848,8 @@ class _VeilidConfigRoutingTable with DiagnosticableTreeMixin implements VeilidConfigRoutingTable { const _VeilidConfigRoutingTable( - {required final List nodeId, - required final List nodeIdSecret, + {required final List publicKeys, + required final List secretKeys, required final List bootstrap, required final List bootstrapKeys, required this.limitOverAttached, @@ -5856,27 +5857,27 @@ class _VeilidConfigRoutingTable required this.limitAttachedStrong, required this.limitAttachedGood, required this.limitAttachedWeak}) - : _nodeId = nodeId, - _nodeIdSecret = nodeIdSecret, + : _publicKeys = publicKeys, + _secretKeys = secretKeys, _bootstrap = bootstrap, _bootstrapKeys = bootstrapKeys; factory _VeilidConfigRoutingTable.fromJson(Map json) => _$VeilidConfigRoutingTableFromJson(json); - final List _nodeId; + final List _publicKeys; @override - List get nodeId { - if (_nodeId is EqualUnmodifiableListView) return _nodeId; + List get publicKeys { + if (_publicKeys is EqualUnmodifiableListView) return _publicKeys; // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_nodeId); + return EqualUnmodifiableListView(_publicKeys); } - final List _nodeIdSecret; + final List _secretKeys; @override - List get nodeIdSecret { - if (_nodeIdSecret is EqualUnmodifiableListView) return _nodeIdSecret; + List get secretKeys { + if (_secretKeys is EqualUnmodifiableListView) return _secretKeys; // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_nodeIdSecret); + return EqualUnmodifiableListView(_secretKeys); } final List _bootstrap; @@ -5926,8 +5927,8 @@ class _VeilidConfigRoutingTable void debugFillProperties(DiagnosticPropertiesBuilder properties) { properties ..add(DiagnosticsProperty('type', 'VeilidConfigRoutingTable')) - ..add(DiagnosticsProperty('nodeId', nodeId)) - ..add(DiagnosticsProperty('nodeIdSecret', nodeIdSecret)) + ..add(DiagnosticsProperty('publicKeys', publicKeys)) + ..add(DiagnosticsProperty('secretKeys', secretKeys)) ..add(DiagnosticsProperty('bootstrap', bootstrap)) ..add(DiagnosticsProperty('bootstrapKeys', bootstrapKeys)) ..add(DiagnosticsProperty('limitOverAttached', limitOverAttached)) @@ -5942,9 +5943,10 @@ class _VeilidConfigRoutingTable return identical(this, other) || (other.runtimeType == runtimeType && other is _VeilidConfigRoutingTable && - const DeepCollectionEquality().equals(other._nodeId, _nodeId) && const DeepCollectionEquality() - .equals(other._nodeIdSecret, _nodeIdSecret) && + .equals(other._publicKeys, _publicKeys) && + const DeepCollectionEquality() + .equals(other._secretKeys, _secretKeys) && const DeepCollectionEquality() .equals(other._bootstrap, _bootstrap) && const DeepCollectionEquality() @@ -5965,8 +5967,8 @@ class _VeilidConfigRoutingTable @override int get hashCode => Object.hash( runtimeType, - const DeepCollectionEquality().hash(_nodeId), - const DeepCollectionEquality().hash(_nodeIdSecret), + const DeepCollectionEquality().hash(_publicKeys), + const DeepCollectionEquality().hash(_secretKeys), const DeepCollectionEquality().hash(_bootstrap), const DeepCollectionEquality().hash(_bootstrapKeys), limitOverAttached, @@ -5977,7 +5979,7 @@ class _VeilidConfigRoutingTable @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'VeilidConfigRoutingTable(nodeId: $nodeId, nodeIdSecret: $nodeIdSecret, bootstrap: $bootstrap, bootstrapKeys: $bootstrapKeys, limitOverAttached: $limitOverAttached, limitFullyAttached: $limitFullyAttached, limitAttachedStrong: $limitAttachedStrong, limitAttachedGood: $limitAttachedGood, limitAttachedWeak: $limitAttachedWeak)'; + return 'VeilidConfigRoutingTable(publicKeys: $publicKeys, secretKeys: $secretKeys, bootstrap: $bootstrap, bootstrapKeys: $bootstrapKeys, limitOverAttached: $limitOverAttached, limitFullyAttached: $limitFullyAttached, limitAttachedStrong: $limitAttachedStrong, limitAttachedGood: $limitAttachedGood, limitAttachedWeak: $limitAttachedWeak)'; } } @@ -5990,8 +5992,8 @@ abstract mixin class _$VeilidConfigRoutingTableCopyWith<$Res> @override @useResult $Res call( - {List nodeId, - List nodeIdSecret, + {List publicKeys, + List secretKeys, List bootstrap, List bootstrapKeys, int limitOverAttached, @@ -6014,8 +6016,8 @@ class __$VeilidConfigRoutingTableCopyWithImpl<$Res> @override @pragma('vm:prefer-inline') $Res call({ - Object? nodeId = null, - Object? nodeIdSecret = null, + Object? publicKeys = null, + Object? secretKeys = null, Object? bootstrap = null, Object? bootstrapKeys = null, Object? limitOverAttached = null, @@ -6025,13 +6027,13 @@ class __$VeilidConfigRoutingTableCopyWithImpl<$Res> Object? limitAttachedWeak = null, }) { return _then(_VeilidConfigRoutingTable( - nodeId: null == nodeId - ? _self._nodeId - : nodeId // ignore: cast_nullable_to_non_nullable + publicKeys: null == publicKeys + ? _self._publicKeys + : publicKeys // ignore: cast_nullable_to_non_nullable as List, - nodeIdSecret: null == nodeIdSecret - ? _self._nodeIdSecret - : nodeIdSecret // ignore: cast_nullable_to_non_nullable + secretKeys: null == secretKeys + ? _self._secretKeys + : secretKeys // ignore: cast_nullable_to_non_nullable as List, bootstrap: null == bootstrap ? _self._bootstrap diff --git a/veilid-flutter/lib/veilid_config.g.dart b/veilid-flutter/lib/veilid_config.g.dart index 1e6b8e62..4f25dc09 100644 --- a/veilid-flutter/lib/veilid_config.g.dart +++ b/veilid-flutter/lib/veilid_config.g.dart @@ -423,10 +423,10 @@ Map _$VeilidConfigRPCToJson(_VeilidConfigRPC instance) => _VeilidConfigRoutingTable _$VeilidConfigRoutingTableFromJson( Map json) => _VeilidConfigRoutingTable( - nodeId: (json['node_id'] as List) + publicKeys: (json['public_keys'] as List) .map(Typed.fromJson) .toList(), - nodeIdSecret: (json['node_id_secret'] as List) + secretKeys: (json['secret_keys'] as List) .map(Typed.fromJson) .toList(), bootstrap: @@ -444,8 +444,8 @@ _VeilidConfigRoutingTable _$VeilidConfigRoutingTableFromJson( Map _$VeilidConfigRoutingTableToJson( _VeilidConfigRoutingTable instance) => { - 'node_id': instance.nodeId.map((e) => e.toJson()).toList(), - 'node_id_secret': instance.nodeIdSecret.map((e) => e.toJson()).toList(), + 'public_keys': instance.publicKeys.map((e) => e.toJson()).toList(), + 'secret_keys': instance.secretKeys.map((e) => e.toJson()).toList(), 'bootstrap': instance.bootstrap, 'bootstrap_keys': instance.bootstrapKeys.map((e) => e.toJson()).toList(), 'limit_over_attached': instance.limitOverAttached, diff --git a/veilid-flutter/lib/veilid_crypto.dart b/veilid-flutter/lib/veilid_crypto.dart index 6eb08c7c..0dee45cf 100644 --- a/veilid-flutter/lib/veilid_crypto.dart +++ b/veilid-flutter/lib/veilid_crypto.dart @@ -86,9 +86,7 @@ class BareKeyPair extends Equatable { factory BareKeyPair.fromString(String s) { final parts = s.split(':'); - if (parts.length != 2 || - parts[0].codeUnits.length != 43 || - parts[1].codeUnits.length != 43) { + if (parts.length != 2) { throw const FormatException('malformed string'); } final key = BarePublicKey.fromString(parts[0]); @@ -110,47 +108,48 @@ class BareKeyPair extends Equatable { @immutable class KeyPair extends Equatable { - const KeyPair({required this.kind, required this.key, required this.secret}); + KeyPair({required this.key, required this.secret}) + : assert(key.kind == secret.kind, 'keypair parts must have same kind'); factory KeyPair.fromString(String s) { final parts = s.split(':'); - if (parts.length != 3 || - parts[0].codeUnits.length != 4 || - parts[1].codeUnits.length != 43 || - parts[2].codeUnits.length != 43) { + if (parts.length != 3 || parts[0].codeUnits.length != 4) { throw VeilidAPIExceptionInvalidArgument('malformed string', 's', s); } final kind = cryptoKindFromString(parts[0]); - final key = BarePublicKey.fromString(parts[1]); - final secret = BareSecretKey.fromString(parts[2]); - return KeyPair(kind: kind, key: key, secret: secret); + final key = + PublicKey(kind: kind, value: BarePublicKey.fromString(parts[1])); + final secret = + SecretKey(kind: kind, value: BareSecretKey.fromString(parts[2])); + return KeyPair(key: key, secret: secret); } factory KeyPair.fromJson(dynamic json) => KeyPair.fromString(json as String); factory KeyPair.fromBareKeyPair(CryptoKind kind, BareKeyPair keyPair) => - KeyPair(kind: kind, key: keyPair.key, secret: keyPair.secret); + KeyPair( + key: PublicKey(kind: kind, value: keyPair.key), + secret: SecretKey(kind: kind, value: keyPair.secret)); factory KeyPair.fromPublicAndBareSecret( PublicKey key, BareSecretKey secret) => - KeyPair(kind: key.kind, key: key.value, secret: secret); - final CryptoKind kind; - final BarePublicKey key; - final BareSecretKey secret; + KeyPair(key: key, secret: SecretKey(kind: key.kind, value: secret)); + final PublicKey key; + final SecretKey secret; @override - List get props => [kind, key, secret]; + List get props => [key, secret]; @override - String toString() => '${cryptoKindToString(kind)}:$key:$secret'; + String toString() => + '${cryptoKindToString(key.kind)}:${key.value}:${secret.value}'; String toJson() => toString(); - BareKeyPair toKeyPair() => BareKeyPair(key: key, secret: secret); + BareKeyPair toBareKeyPair() => + BareKeyPair(key: key.value, secret: secret.value); } typedef PublicKey = Typed; typedef Signature = Typed; -typedef Nonce = Typed; typedef SecretKey = Typed; typedef HashDigest = Typed; typedef SharedSecret = Typed; -typedef HashDistance = Typed; typedef RecordKey = Typed; typedef RouteId = Typed; typedef NodeId = Typed; @@ -164,22 +163,21 @@ abstract class VeilidCryptoSystem { // Cached Operations - Future cachedDH(BarePublicKey key, BareSecretKey secret); + Future cachedDH(PublicKey key, SecretKey secret); // Generation Future randomBytes(int len); Future hashPassword(Uint8List password, Uint8List salt); Future verifyPassword(Uint8List password, String passwordHash); - Future deriveSharedSecret( - Uint8List password, Uint8List salt); - Future randomNonce(); - Future randomSharedSecret(); - Future computeDH(BarePublicKey key, BareSecretKey secret); - Future generateSharedSecret( - BarePublicKey key, BareSecretKey secret, Uint8List domain); - Future generateKeyPair(); - Future generateHash(Uint8List data); + Future deriveSharedSecret(Uint8List password, Uint8List salt); + Future randomNonce(); + Future randomSharedSecret(); + Future computeDH(PublicKey key, SecretKey secret); + Future generateSharedSecret( + PublicKey key, SecretKey secret, Uint8List domain); + Future generateKeyPair(); + Future generateHash(Uint8List data); //Future generateHashReader(Stream> reader); // Validation @@ -193,45 +191,39 @@ abstract class VeilidCryptoSystem { Future aeadOverhead(); Future defaultSaltLength(); - Future checkSharedSecret(BareSharedSecret secret); - Future checkNonce(BareNonce nonce); - Future checkHashDigest(BareHashDigest digest); - Future checkPublicKey(BarePublicKey key); - Future checkSecretKey(BareSecretKey key); - Future checkSignature(BareSignature signature); + Future checkSharedSecret(SharedSecret secret); + Future checkNonce(Nonce nonce); + Future checkHashDigest(HashDigest digest); + Future checkPublicKey(PublicKey key); + Future checkSecretKey(SecretKey key); + Future checkSignature(Signature signature); - Future validateKeyPair(BarePublicKey key, BareSecretKey secret); - Future validateKeyPairWithKeyPair(BareKeyPair keyPair) => + Future validateKeyPair(PublicKey key, SecretKey secret); + Future validateKeyPairWithKeyPair(KeyPair keyPair) => validateKeyPair(keyPair.key, keyPair.secret); - Future validateHash(Uint8List data, BareHashDigest hash); + Future validateHash(Uint8List data, HashDigest hash); //Future validateHashReader(Stream> reader, HashDigest hash); - // Distance Metric - - Future distance(BareHashDigest key1, BareHashDigest key2); - // Authentication - Future sign( - BarePublicKey key, BareSecretKey secret, Uint8List data); - Future signWithKeyPair(BareKeyPair keyPair, Uint8List data) => + Future sign(PublicKey key, SecretKey secret, Uint8List data); + Future signWithKeyPair(KeyPair keyPair, Uint8List data) => sign(keyPair.key, keyPair.secret, data); - Future verify( - BarePublicKey key, Uint8List data, BareSignature signature); + Future verify(PublicKey key, Uint8List data, Signature signature); // AEAD Encrypt/Decrypt - Future decryptAead(Uint8List body, BareNonce nonce, - BareSharedSecret sharedSecret, Uint8List? associatedData); - Future encryptAead(Uint8List body, BareNonce nonce, - BareSharedSecret sharedSecret, Uint8List? associatedData); + Future decryptAead(Uint8List body, Nonce nonce, + SharedSecret sharedSecret, Uint8List? associatedData); + Future encryptAead(Uint8List body, Nonce nonce, + SharedSecret sharedSecret, Uint8List? associatedData); Future cryptNoAuth( - Uint8List body, BareNonce nonce, BareSharedSecret sharedSecret); + Uint8List body, Nonce nonce, SharedSecret sharedSecret); Future encryptAeadWithNonce( - Uint8List body, BareSharedSecret secret) async { + Uint8List body, SharedSecret secret) async { // generate nonce final nonce = await randomNonce(); // crypt and append nonce @@ -242,12 +234,12 @@ abstract class VeilidCryptoSystem { } Future decryptAeadWithNonce( - Uint8List body, BareSharedSecret secret) async { + Uint8List body, SharedSecret secret) async { final nlen = await nonceLength(); if (body.length < nlen) { throw const FormatException('not enough data to decrypt'); } - final nonce = BareNonce.fromBytes(body.sublist(body.length - nlen)); + final nonce = Nonce.fromBytes(body.sublist(body.length - nlen)); final encryptedData = body.sublist(0, body.length - nlen); // decrypt return decryptAead(encryptedData, nonce, secret, null); @@ -272,7 +264,7 @@ abstract class VeilidCryptoSystem { final ekbytes = Uint8List.fromList(utf8.encode(password)); final bodyBytes = body.sublist(0, body.length - nlen); final saltBytes = body.sublist(body.length - nlen); - final nonce = BareNonce.fromBytes(saltBytes); + final nonce = Nonce.fromBytes(saltBytes); final sharedSecret = await deriveSharedSecret(ekbytes, saltBytes); return decryptAead(bodyBytes, nonce, sharedSecret, null); } @@ -280,7 +272,7 @@ abstract class VeilidCryptoSystem { // NoAuth Encrypt/Decrypt Future encryptNoAuthWithNonce( - Uint8List body, BareSharedSecret secret) async { + Uint8List body, SharedSecret secret) async { // generate nonce final nonce = await randomNonce(); // crypt and append nonce @@ -291,12 +283,12 @@ abstract class VeilidCryptoSystem { } Future decryptNoAuthWithNonce( - Uint8List body, BareSharedSecret secret) async { + Uint8List body, SharedSecret secret) async { final nlen = await nonceLength(); if (body.length < nlen) { throw const FormatException('not enough data to decrypt'); } - final nonce = BareNonce.fromBytes(body.sublist(body.length - nlen)); + final nonce = Nonce.fromBytes(body.sublist(body.length - nlen)); final encryptedData = body.sublist(0, body.length - nlen); // decrypt return cryptNoAuth(encryptedData, nonce, secret); @@ -321,7 +313,7 @@ abstract class VeilidCryptoSystem { final ekbytes = Uint8List.fromList(utf8.encode(password)); final bodyBytes = body.sublist(0, body.length - nlen); final saltBytes = body.sublist(body.length - nlen); - final nonce = BareNonce.fromBytes(saltBytes); + final nonce = Nonce.fromBytes(saltBytes); final sharedSecret = await deriveSharedSecret(ekbytes, saltBytes); return cryptNoAuth(bodyBytes, nonce, sharedSecret); } diff --git a/veilid-flutter/lib/veilid_encoding.dart b/veilid-flutter/lib/veilid_encoding.dart index 198a36e8..ebc58008 100644 --- a/veilid-flutter/lib/veilid_encoding.dart +++ b/veilid-flutter/lib/veilid_encoding.dart @@ -68,8 +68,8 @@ sealed class EncodedString extends Equatable { return BarePublicKey.fromBytes(bytes) as T; case const (BareSignature): return BareSignature.fromBytes(bytes) as T; - case const (BareNonce): - return BareNonce.fromBytes(bytes) as T; + case const (Nonce): + return Nonce.fromBytes(bytes) as T; case const (BareSecretKey): return BareSecretKey.fromBytes(bytes) as T; case const (BareHashDigest): @@ -97,8 +97,8 @@ sealed class EncodedString extends Equatable { return BarePublicKey.fromString(s) as T; case const (BareSignature): return BareSignature.fromString(s) as T; - case const (BareNonce): - return BareNonce.fromString(s) as T; + case const (Nonce): + return Nonce.fromString(s) as T; case const (BareSecretKey): return BareSecretKey.fromString(s) as T; case const (BareHashDigest): @@ -126,8 +126,8 @@ sealed class EncodedString extends Equatable { return BarePublicKey.fromJson(json) as T; case const (BareSignature): return BareSignature.fromJson(json) as T; - case const (BareNonce): - return BareNonce.fromJson(json) as T; + case const (Nonce): + return Nonce.fromJson(json) as T; case const (BareSecretKey): return BareSecretKey.fromJson(json) as T; case const (BareHashDigest): @@ -168,10 +168,10 @@ class BareSignature extends EncodedString { BareSignature.fromJson(super.json) : super._fromJson(); } -class BareNonce extends EncodedString { - BareNonce.fromBytes(super.bytes) : super._fromBytes(); - BareNonce.fromString(super.s) : super._fromString(); - BareNonce.fromJson(super.json) : super._fromJson(); +class Nonce extends EncodedString { + Nonce.fromBytes(super.bytes) : super._fromBytes(); + Nonce.fromString(super.s) : super._fromString(); + Nonce.fromJson(super.json) : super._fromJson(); } class BareSecretKey extends EncodedString { diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart index 77a87ac4..caea6780 100644 --- a/veilid-flutter/lib/veilid_ffi.dart +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -73,7 +73,7 @@ typedef _RoutingContextGetDHTRecordKeyDart = void Function( // fn routing_context_create_dht_record(port: i64, // id: u32, schema: FfiStr, owner: FfiStr, kind: u32) typedef _RoutingContextCreateDHTRecordDart = void Function( - int, int, Pointer, Pointer, int); + int, int, int, Pointer, Pointer); // fn routing_context_open_dht_record(port: i64, // id: u32, key: FfiStr, writer: FfiStr) typedef _RoutingContextOpenDHTRecordDart = void Function( @@ -155,8 +155,6 @@ typedef _TableDbTransactionDeleteDart = void Function( int, int, int, Pointer); // fn valid_crypto_kinds() -> *mut c_char typedef _ValidCryptoKindsDart = Pointer Function(); -// fn best_crypto_kind() -> u32 -typedef _BestCryptoKindDart = int Function(); // fn verify_signatures(port: i64, // node_ids: FfiStr, data: FfiStr, signatures: FfiStr) typedef _VerifySignaturesDart = void Function( @@ -234,9 +232,6 @@ typedef _CryptoValidateKeyPairDart = void Function( // fn crypto_validate_hash(port: i64, kind: u32, data: FfiStr, hash: FfiStr) typedef _CryptoValidateHashDart = void Function( int, int, Pointer, Pointer); -// fn crypto_distance(port: i64, kind: u32, key1: FfiStr, key2: FfiStr) -typedef _CryptoDistanceDart = void Function( - int, int, Pointer, Pointer); // fn crypto_sign(port: i64, // kind: u32, key: FfiStr, secret: FfiStr, data: FfiStr) typedef _CryptoSignDart = void Function( @@ -673,8 +668,8 @@ class VeilidRoutingContextFFI extends VeilidRoutingContext { } @override - Future createDHTRecord(DHTSchema schema, - {KeyPair? owner, CryptoKind kind = 0}) async { + Future createDHTRecord(CryptoKind kind, DHTSchema schema, + {KeyPair? owner}) async { _ctx.ensureValid(); final nativeSchema = jsonEncode(schema).toNativeUtf8(); final nativeOwner = @@ -682,7 +677,7 @@ class VeilidRoutingContextFFI extends VeilidRoutingContext { final recvPort = ReceivePort('routing_context_create_dht_record'); final sendPort = recvPort.sendPort; _ctx.ffi._routingContextCreateDHTRecord( - sendPort.nativePort, _ctx.id!, nativeSchema, nativeOwner, kind); + sendPort.nativePort, _ctx.id!, kind, nativeSchema, nativeOwner); final dhtRecordDescriptor = await processFutureJson(DHTRecordDescriptor.fromJson, recvPort.first); return dhtRecordDescriptor; @@ -1038,30 +1033,30 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { CryptoKind kind() => _kind; @override - Future cachedDH(BarePublicKey key, BareSecretKey secret) { + Future cachedDH(PublicKey key, SecretKey secret) { final nativeKey = jsonEncode(key).toNativeUtf8(); final nativeSecret = jsonEncode(secret).toNativeUtf8(); final recvPort = ReceivePort('crypto_cached_dh'); final sendPort = recvPort.sendPort; _ffi._cryptoCachedDH(sendPort.nativePort, _kind, nativeKey, nativeSecret); - return processFutureJson(BareSharedSecret.fromJson, recvPort.first); + return processFutureJson(SharedSecret.fromJson, recvPort.first); } @override - Future computeDH(BarePublicKey key, BareSecretKey secret) { + Future computeDH(PublicKey key, SecretKey secret) { final nativeKey = jsonEncode(key).toNativeUtf8(); final nativeSecret = jsonEncode(secret).toNativeUtf8(); final recvPort = ReceivePort('crypto_compute_dh'); final sendPort = recvPort.sendPort; _ffi._cryptoComputeDH(sendPort.nativePort, _kind, nativeKey, nativeSecret); - return processFutureJson(BareSharedSecret.fromJson, recvPort.first); + return processFutureJson(SharedSecret.fromJson, recvPort.first); } @override - Future generateSharedSecret( - BarePublicKey key, BareSecretKey secret, Uint8List domain) { + Future generateSharedSecret( + PublicKey key, SecretKey secret, Uint8List domain) { final nativeKey = jsonEncode(key).toNativeUtf8(); final nativeSecret = jsonEncode(secret).toNativeUtf8(); final nativeDomain = base64UrlNoPadEncode(domain).toNativeUtf8(); @@ -1070,7 +1065,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { final sendPort = recvPort.sendPort; _ffi._cryptoGenerateSharedSecret( sendPort.nativePort, _kind, nativeKey, nativeSecret, nativeDomain); - return processFutureJson(BareSharedSecret.fromJson, recvPort.first); + return processFutureJson(SharedSecret.fromJson, recvPort.first); } @override @@ -1150,7 +1145,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future checkSharedSecret(BareSharedSecret secret) { + Future checkSharedSecret(SharedSecret secret) { final nativeSecret = jsonEncode(secret).toNativeUtf8(); final recvPort = ReceivePort('crypto_check_shared_secret'); final sendPort = recvPort.sendPort; @@ -1159,7 +1154,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future checkNonce(BareNonce nonce) { + Future checkNonce(Nonce nonce) { final nativeNonce = jsonEncode(nonce).toNativeUtf8(); final recvPort = ReceivePort('crypto_check_nonce'); final sendPort = recvPort.sendPort; @@ -1168,7 +1163,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future checkHashDigest(BareHashDigest digest) { + Future checkHashDigest(HashDigest digest) { final nativeDigest = jsonEncode(digest).toNativeUtf8(); final recvPort = ReceivePort('crypto_check_hash_digest'); final sendPort = recvPort.sendPort; @@ -1177,7 +1172,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future checkPublicKey(BarePublicKey key) { + Future checkPublicKey(PublicKey key) { final nativeKey = jsonEncode(key).toNativeUtf8(); final recvPort = ReceivePort('crypto_check_public_key'); final sendPort = recvPort.sendPort; @@ -1186,7 +1181,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future checkSecretKey(BareSecretKey key) { + Future checkSecretKey(SecretKey key) { final nativeKey = jsonEncode(key).toNativeUtf8(); final recvPort = ReceivePort('crypto_check_secret_key'); final sendPort = recvPort.sendPort; @@ -1195,7 +1190,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future checkSignature(BareSignature signature) { + Future checkSignature(Signature signature) { final nativeSignature = jsonEncode(signature).toNativeUtf8(); final recvPort = ReceivePort('crypto_check_signature'); final sendPort = recvPort.sendPort; @@ -1228,8 +1223,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future deriveSharedSecret( - Uint8List password, Uint8List salt) { + Future deriveSharedSecret(Uint8List password, Uint8List salt) { final nativeEncodedPassword = base64UrlNoPadEncode(password).toNativeUtf8(); final nativeEncodedSalt = base64UrlNoPadEncode(salt).toNativeUtf8(); @@ -1237,45 +1231,45 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { final sendPort = recvPort.sendPort; _ffi._cryptoDeriveSharedSecret( sendPort.nativePort, _kind, nativeEncodedPassword, nativeEncodedSalt); - return processFutureJson(BareSharedSecret.fromJson, recvPort.first); + return processFutureJson(SharedSecret.fromJson, recvPort.first); } @override - Future randomNonce() { + Future randomNonce() { final recvPort = ReceivePort('crypto_random_nonce'); final sendPort = recvPort.sendPort; _ffi._cryptoRandomNonce(sendPort.nativePort, _kind); - return processFutureJson(BareNonce.fromJson, recvPort.first); + return processFutureJson(Nonce.fromJson, recvPort.first); } @override - Future randomSharedSecret() { + Future randomSharedSecret() { final recvPort = ReceivePort('crypto_random_shared_secret'); final sendPort = recvPort.sendPort; _ffi._cryptoRandomSharedSecret(sendPort.nativePort, _kind); - return processFutureJson(BareSharedSecret.fromJson, recvPort.first); + return processFutureJson(SharedSecret.fromJson, recvPort.first); } @override - Future generateKeyPair() { + Future generateKeyPair() { final recvPort = ReceivePort('crypto_generate_key_pair'); final sendPort = recvPort.sendPort; _ffi._cryptoGenerateKeyPair(sendPort.nativePort, _kind); - return processFutureJson(BareKeyPair.fromJson, recvPort.first); + return processFutureJson(KeyPair.fromJson, recvPort.first); } @override - Future generateHash(Uint8List data) { + Future generateHash(Uint8List data) { final nativeEncodedData = base64UrlNoPadEncode(data).toNativeUtf8(); final recvPort = ReceivePort('crypto_generate_hash'); final sendPort = recvPort.sendPort; _ffi._cryptoGenerateHash(sendPort.nativePort, _kind, nativeEncodedData); - return processFutureJson(BareHashDigest.fromJson, recvPort.first); + return processFutureJson(HashDigest.fromJson, recvPort.first); } @override - Future validateKeyPair(BarePublicKey key, BareSecretKey secret) { + Future validateKeyPair(PublicKey key, SecretKey secret) { final nativeKey = jsonEncode(key).toNativeUtf8(); final nativeSecret = jsonEncode(secret).toNativeUtf8(); @@ -1287,7 +1281,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future validateHash(Uint8List data, BareHashDigest hash) { + Future validateHash(Uint8List data, HashDigest hash) { final nativeEncodedData = base64UrlNoPadEncode(data).toNativeUtf8(); final nativeHash = jsonEncode(hash).toNativeUtf8(); @@ -1299,19 +1293,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future distance(BareHashDigest key1, BareHashDigest key2) { - final nativeKey1 = jsonEncode(key1).toNativeUtf8(); - final nativeKey2 = jsonEncode(key2).toNativeUtf8(); - - final recvPort = ReceivePort('crypto_distance'); - final sendPort = recvPort.sendPort; - _ffi._cryptoDistance(sendPort.nativePort, _kind, nativeKey1, nativeKey2); - return processFutureJson(BareHashDistance.fromJson, recvPort.first); - } - - @override - Future sign( - BarePublicKey key, BareSecretKey secret, Uint8List data) { + Future sign(PublicKey key, SecretKey secret, Uint8List data) { final nativeKey = jsonEncode(key).toNativeUtf8(); final nativeSecret = jsonEncode(secret).toNativeUtf8(); final nativeEncodedData = base64UrlNoPadEncode(data).toNativeUtf8(); @@ -1320,12 +1302,11 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { final sendPort = recvPort.sendPort; _ffi._cryptoSign( sendPort.nativePort, _kind, nativeKey, nativeSecret, nativeEncodedData); - return processFutureJson(BareSignature.fromJson, recvPort.first); + return processFutureJson(Signature.fromJson, recvPort.first); } @override - Future verify( - BarePublicKey key, Uint8List data, BareSignature signature) { + Future verify(PublicKey key, Uint8List data, Signature signature) { final nativeKey = jsonEncode(key).toNativeUtf8(); final nativeEncodedData = base64UrlNoPadEncode(data).toNativeUtf8(); final nativeSignature = jsonEncode(signature).toNativeUtf8(); @@ -1338,8 +1319,8 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future decryptAead(Uint8List body, BareNonce nonce, - BareSharedSecret sharedSecret, Uint8List? associatedData) async { + Future decryptAead(Uint8List body, Nonce nonce, + SharedSecret sharedSecret, Uint8List? associatedData) async { final nativeEncodedBody = base64UrlNoPadEncode(body).toNativeUtf8(); final nativeNonce = jsonEncode(nonce).toNativeUtf8(); final nativeSharedSecret = jsonEncode(sharedSecret).toNativeUtf8(); @@ -1356,8 +1337,8 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { } @override - Future encryptAead(Uint8List body, BareNonce nonce, - BareSharedSecret sharedSecret, Uint8List? associatedData) async { + Future encryptAead(Uint8List body, Nonce nonce, + SharedSecret sharedSecret, Uint8List? associatedData) async { final nativeEncodedBody = base64UrlNoPadEncode(body).toNativeUtf8(); final nativeNonce = jsonEncode(nonce).toNativeUtf8(); final nativeSharedSecret = jsonEncode(sharedSecret).toNativeUtf8(); @@ -1375,7 +1356,7 @@ class VeilidCryptoSystemFFI extends VeilidCryptoSystem { @override Future cryptNoAuth( - Uint8List body, BareNonce nonce, BareSharedSecret sharedSecret) async { + Uint8List body, Nonce nonce, SharedSecret sharedSecret) async { final nativeEncodedBody = base64UrlNoPadEncode(body).toNativeUtf8(); final nativeNonce = jsonEncode(nonce).toNativeUtf8(); final nativeSharedSecret = jsonEncode(sharedSecret).toNativeUtf8(); @@ -1452,7 +1433,7 @@ class VeilidFFI extends Veilid { _routingContextCreateDHTRecord = dylib.lookupFunction< Void Function( - Int64, Uint32, Pointer, Pointer, Uint32), + Int64, Uint32, Uint32, Pointer, Pointer), _RoutingContextCreateDHTRecordDart>( 'routing_context_create_dht_record'), _routingContextOpenDHTRecord = dylib.lookupFunction< @@ -1549,9 +1530,6 @@ class VeilidFFI extends Veilid { _TableDbTransactionDeleteDart>('table_db_transaction_delete'), _validCryptoKinds = dylib.lookupFunction Function(), _ValidCryptoKindsDart>('valid_crypto_kinds'), - _bestCryptoKind = - dylib.lookupFunction( - 'best_crypto_kind'), _verifySignatures = dylib.lookupFunction< Void Function(Int64, Pointer, Pointer, Pointer), _VerifySignaturesDart>('verify_signatures'), @@ -1639,9 +1617,6 @@ class VeilidFFI extends Veilid { _cryptoValidateHash = dylib.lookupFunction< Void Function(Int64, Uint32, Pointer, Pointer), _CryptoValidateHashDart>('crypto_validate_hash'), - _cryptoDistance = dylib.lookupFunction< - Void Function(Int64, Uint32, Pointer, Pointer), - _CryptoDistanceDart>('crypto_distance'), _cryptoSign = dylib.lookupFunction< Void Function( Int64, Uint32, Pointer, Pointer, Pointer), @@ -1745,7 +1720,6 @@ class VeilidFFI extends Veilid { final _TableDbTransactionDeleteDart _tableDbTransactionDelete; final _ValidCryptoKindsDart _validCryptoKinds; - final _BestCryptoKindDart _bestCryptoKind; final _VerifySignaturesDart _verifySignatures; final _GenerateSignaturesDart _generateSignatures; final _GenerateKeyPairDart _generateKeyPair; @@ -1781,7 +1755,6 @@ class VeilidFFI extends Veilid { final _CryptoGenerateHashDart _cryptoGenerateHash; final _CryptoValidateKeyPairDart _cryptoValidateKeyPair; final _CryptoValidateHashDart _cryptoValidateHash; - final _CryptoDistanceDart _cryptoDistance; final _CryptoSignDart _cryptoSign; final _CryptoVerifyDart _cryptoVerify; final _CryptoDecryptAeadDart _cryptoDecryptAead; @@ -1982,10 +1955,6 @@ class VeilidFFI extends Veilid { return VeilidCryptoSystemFFI._(this, kind); } - @override - Future bestCryptoSystem() async => - VeilidCryptoSystemFFI._(this, _bestCryptoKind()); - @override Future?> verifySignatures( List nodeIds, Uint8List data, List signatures) { diff --git a/veilid-flutter/lib/veilid_js.dart b/veilid-flutter/lib/veilid_js.dart index 52191e8e..5b7c3cab 100644 --- a/veilid-flutter/lib/veilid_js.dart +++ b/veilid-flutter/lib/veilid_js.dart @@ -151,15 +151,15 @@ class VeilidRoutingContextJS extends VeilidRoutingContext { } @override - Future createDHTRecord(DHTSchema schema, - {KeyPair? owner, CryptoKind kind = 0}) async { + Future createDHTRecord(CryptoKind kind, DHTSchema schema, + {KeyPair? owner}) async { final id = _ctx.requireId(); - return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util - .callMethod(wasm, 'routing_context_create_dht_record', [ + return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise( + js_util.callMethod(wasm, 'routing_context_create_dht_record', [ id, + kind, jsonEncode(schema), if (owner != null) jsonEncode(owner) else null, - kind ])))); } @@ -279,23 +279,23 @@ class VeilidCryptoSystemJS extends VeilidCryptoSystem { CryptoKind kind() => _kind; @override - Future cachedDH( - BarePublicKey key, BareSecretKey secret) async => - BareSharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util - .callMethod(wasm, 'crypto_cached_dh', - [_kind, jsonEncode(key), jsonEncode(secret)])))); + Future cachedDH(PublicKey key, SecretKey secret) async => + SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod( + wasm, + 'crypto_cached_dh', + [_kind, jsonEncode(key), jsonEncode(secret)])))); @override - Future computeDH( - BarePublicKey key, BareSecretKey secret) async => - BareSharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util - .callMethod(wasm, 'crypto_compute_dh', - [_kind, jsonEncode(key), jsonEncode(secret)])))); + Future computeDH(PublicKey key, SecretKey secret) async => + SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod( + wasm, + 'crypto_compute_dh', + [_kind, jsonEncode(key), jsonEncode(secret)])))); @override - Future generateSharedSecret( - BarePublicKey key, BareSecretKey secret, Uint8List domain) async => - BareSharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util - .callMethod(wasm, 'crypto_generate_shared_secret', [ + Future generateSharedSecret( + PublicKey key, SecretKey secret, Uint8List domain) async => + SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod( + wasm, 'crypto_generate_shared_secret', [ _kind, jsonEncode(key), jsonEncode(secret), @@ -340,29 +340,29 @@ class VeilidCryptoSystemJS extends VeilidCryptoSystem { js_util.callMethod(wasm, 'crypto_aead_overhead', [_kind])); @override - Future checkSharedSecret(BareSharedSecret secret) => + Future checkSharedSecret(SharedSecret secret) => _wrapApiPromise(js_util.callMethod( wasm, 'crypto_check_shared_secret', [_kind, jsonEncode(secret)])); @override - Future checkNonce(BareNonce nonce) => _wrapApiPromise(js_util + Future checkNonce(Nonce nonce) => _wrapApiPromise(js_util .callMethod(wasm, 'crypto_check_nonce', [_kind, jsonEncode(nonce)])); @override - Future checkHashDigest(BareHashDigest digest) => + Future checkHashDigest(HashDigest digest) => _wrapApiPromise(js_util.callMethod( wasm, 'crypto_check_hash_digest', [_kind, jsonEncode(digest)])); @override - Future checkPublicKey(BarePublicKey key) => _wrapApiPromise(js_util + Future checkPublicKey(PublicKey key) => _wrapApiPromise(js_util .callMethod(wasm, 'crypto_check_public_key', [_kind, jsonEncode(key)])); @override - Future checkSecretKey(BareSecretKey key) => _wrapApiPromise(js_util + Future checkSecretKey(SecretKey key) => _wrapApiPromise(js_util .callMethod(wasm, 'crypto_check_secret_key', [_kind, jsonEncode(key)])); @override - Future checkSignature(BareSignature signature) => + Future checkSignature(Signature signature) => _wrapApiPromise(js_util.callMethod( wasm, 'crypto_check_signature', [_kind, jsonEncode(signature)])); @@ -377,58 +377,50 @@ class VeilidCryptoSystemJS extends VeilidCryptoSystem { [_kind, base64UrlNoPadEncode(password), passwordHash])); @override - Future deriveSharedSecret( + Future deriveSharedSecret( Uint8List password, Uint8List salt) async => - BareSharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util - .callMethod(wasm, 'crypto_derive_shared_secret', [ + SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod( + wasm, 'crypto_derive_shared_secret', [ _kind, base64UrlNoPadEncode(password), base64UrlNoPadEncode(salt) ])))); @override - Future randomNonce() async => - BareNonce.fromJson(jsonDecode(await _wrapApiPromise( + Future randomNonce() async => + Nonce.fromJson(jsonDecode(await _wrapApiPromise( js_util.callMethod(wasm, 'crypto_random_nonce', [_kind])))); @override - Future randomSharedSecret() async => - BareSharedSecret.fromJson(jsonDecode(await _wrapApiPromise( + Future randomSharedSecret() async => + SharedSecret.fromJson(jsonDecode(await _wrapApiPromise( js_util.callMethod(wasm, 'crypto_random_shared_secret', [_kind])))); @override - Future generateKeyPair() async => - BareKeyPair.fromJson(jsonDecode(await _wrapApiPromise( + Future generateKeyPair() async => + KeyPair.fromJson(jsonDecode(await _wrapApiPromise( js_util.callMethod(wasm, 'crypto_generate_key_pair', [_kind])))); @override - Future generateHash(Uint8List data) async => - BareHashDigest.fromJson(jsonDecode(await _wrapApiPromise(js_util - .callMethod(wasm, 'crypto_generate_hash', - [_kind, base64UrlNoPadEncode(data)])))); + Future generateHash(Uint8List data) async => + HashDigest.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod( + wasm, 'crypto_generate_hash', [_kind, base64UrlNoPadEncode(data)])))); @override - Future validateKeyPair(BarePublicKey key, BareSecretKey secret) => + Future validateKeyPair(PublicKey key, SecretKey secret) => _wrapApiPromise(js_util.callMethod(wasm, 'crypto_validate_key_pair', [_kind, jsonEncode(key), jsonEncode(secret)])); @override - Future validateHash(Uint8List data, BareHashDigest hash) => + Future validateHash(Uint8List data, HashDigest hash) => _wrapApiPromise(js_util.callMethod(wasm, 'crypto_validate_hash', [_kind, base64UrlNoPadEncode(data), jsonEncode(hash)])); @override - Future distance( - BareHashDigest key1, BareHashDigest key2) async => - BareHashDistance.fromJson(jsonDecode(await _wrapApiPromise(js_util - .callMethod(wasm, 'crypto_distance', - [_kind, jsonEncode(key1), jsonEncode(key2)])))); - - @override - Future sign( - BarePublicKey key, BareSecretKey secret, Uint8List data) async => - BareSignature.fromJson(jsonDecode(await _wrapApiPromise(js_util - .callMethod(wasm, 'crypto_sign', [ + Future sign( + PublicKey key, SecretKey secret, Uint8List data) async => + Signature.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod( + wasm, 'crypto_sign', [ _kind, jsonEncode(key), jsonEncode(secret), @@ -436,8 +428,7 @@ class VeilidCryptoSystemJS extends VeilidCryptoSystem { ])))); @override - Future verify( - BarePublicKey key, Uint8List data, BareSignature signature) => + Future verify(PublicKey key, Uint8List data, Signature signature) => _wrapApiPromise(js_util.callMethod(wasm, 'crypto_verify', [ _kind, jsonEncode(key), @@ -446,8 +437,8 @@ class VeilidCryptoSystemJS extends VeilidCryptoSystem { ])); @override - Future decryptAead(Uint8List body, BareNonce nonce, - BareSharedSecret sharedSecret, Uint8List? associatedData) async => + Future decryptAead(Uint8List body, Nonce nonce, + SharedSecret sharedSecret, Uint8List? associatedData) async => base64UrlNoPadDecode(await _wrapApiPromise( js_util.callMethod(wasm, 'crypto_decrypt_aead', [ _kind, @@ -461,8 +452,8 @@ class VeilidCryptoSystemJS extends VeilidCryptoSystem { ]))); @override - Future encryptAead(Uint8List body, BareNonce nonce, - BareSharedSecret sharedSecret, Uint8List? associatedData) async => + Future encryptAead(Uint8List body, Nonce nonce, + SharedSecret sharedSecret, Uint8List? associatedData) async => base64UrlNoPadDecode(await _wrapApiPromise( js_util.callMethod(wasm, 'crypto_encrypt_aead', [ _kind, @@ -476,8 +467,8 @@ class VeilidCryptoSystemJS extends VeilidCryptoSystem { ]))); @override - Future cryptNoAuth(Uint8List body, BareNonce nonce, - BareSharedSecret sharedSecret) async => + Future cryptNoAuth( + Uint8List body, Nonce nonce, SharedSecret sharedSecret) async => base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod( wasm, 'crypto_crypt_no_auth', [ _kind, @@ -728,10 +719,6 @@ class VeilidJS extends Veilid { return VeilidCryptoSystemJS._(this, kind); } - @override - Future bestCryptoSystem() async => VeilidCryptoSystemJS._( - this, js_util.callMethod(wasm, 'best_crypto_kind', [])); - @override Future?> verifySignatures(List nodeIds, Uint8List data, List signatures) async => diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 8ec19e17..63003fd4 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -133,13 +133,6 @@ pub struct VeilidFFIConfig { pub logging: VeilidFFIConfigLogging, } -#[derive(Debug, Deserialize, Serialize)] -pub struct VeilidFFIRouteBlob { - pub route_id: veilid_core::RouteId, - #[serde(with = "veilid_core::as_human_base64")] - pub blob: Vec, -} - ///////////////////////////////////////// // Initializer #[no_mangle] @@ -704,15 +697,11 @@ pub extern "C" fn routing_context_get_dht_record_key( pub extern "C" fn routing_context_create_dht_record( port: i64, id: u32, + kind: u32, schema: FfiStr, owner: FfiStr, - kind: u32, ) { - let crypto_kind = if kind == 0 { - None - } else { - Some(veilid_core::CryptoKind::from(kind)) - }; + let crypto_kind = veilid_core::CryptoKind::from(kind); let schema: veilid_core::DHTSchema = veilid_core::deserialize_opt_json(schema.into_opt_string()).unwrap(); let owner: Option = owner @@ -724,7 +713,7 @@ pub extern "C" fn routing_context_create_dht_record( let routing_context = get_routing_context(id, "routing_context_create_dht_record")?; let dht_record_descriptor = routing_context - .create_dht_record(schema, owner, crypto_kind) + .create_dht_record(crypto_kind, schema, owner) .await?; APIResult::Ok(dht_record_descriptor) } @@ -946,9 +935,7 @@ pub extern "C" fn new_private_route(port: i64) { async move { let veilid_api = get_veilid_api().await?; - let (route_id, blob) = veilid_api.new_private_route().await?; - - let route_blob = VeilidFFIRouteBlob { route_id, blob }; + let route_blob = veilid_api.new_private_route().await?; APIResult::Ok(route_blob) } @@ -968,12 +955,10 @@ pub extern "C" fn new_custom_private_route(port: i64, stability: FfiStr, sequenc async move { let veilid_api = get_veilid_api().await?; - let (route_id, blob) = veilid_api + let route_blob = veilid_api .new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing) .await?; - let route_blob = VeilidFFIRouteBlob { route_id, blob }; - APIResult::Ok(route_blob) } .in_current_span(), @@ -1321,12 +1306,6 @@ pub extern "C" fn valid_crypto_kinds() -> *mut c_char { .into_ffi_value() } -#[no_mangle] -#[instrument(level = "trace", target = "ffi", skip_all)] -pub extern "C" fn best_crypto_kind() -> u32 { - veilid_core::best_crypto_kind().into() -} - #[no_mangle] #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn verify_signatures(port: i64, node_ids: FfiStr, data: FfiStr, signatures: FfiStr) { @@ -1365,9 +1344,7 @@ pub extern "C" fn generate_signatures(port: i64, data: FfiStr, key_pairs: FfiStr async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let out = crypto.generate_signatures(&data, &key_pairs, |k, s| { - veilid_core::Signature::new(k.kind(), s) - })?; + let out = crypto.generate_signatures(&data, &key_pairs, |_k, s| s)?; APIResult::Ok(out) } .in_current_span(), @@ -1392,9 +1369,9 @@ pub extern "C" fn generate_key_pair(port: i64, kind: u32) { pub extern "C" fn crypto_cached_dh(port: i64, kind: u32, key: FfiStr, secret: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BarePublicKey = + let key: veilid_core::PublicKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - let secret: veilid_core::BareSecretKey = + let secret: veilid_core::SecretKey = veilid_core::deserialize_opt_json(secret.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result_json( @@ -1420,9 +1397,9 @@ pub extern "C" fn crypto_cached_dh(port: i64, kind: u32, key: FfiStr, secret: Ff pub extern "C" fn crypto_compute_dh(port: i64, kind: u32, key: FfiStr, secret: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BarePublicKey = + let key: veilid_core::PublicKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - let secret: veilid_core::BareSecretKey = + let secret: veilid_core::SecretKey = veilid_core::deserialize_opt_json(secret.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result_json( @@ -1454,9 +1431,9 @@ pub extern "C" fn crypto_generate_shared_secret( ) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BarePublicKey = + let key: veilid_core::PublicKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - let secret: veilid_core::BareSecretKey = + let secret: veilid_core::SecretKey = veilid_core::deserialize_opt_json(secret.into_opt_string()).unwrap(); let domain: Vec = data_encoding::BASE64URL_NOPAD .decode(domain.into_opt_string().unwrap().as_bytes()) @@ -1692,7 +1669,7 @@ pub extern "C" fn crypto_aead_overhead(port: i64, kind: u32) { #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn crypto_check_shared_secret(port: i64, kind: u32, secret: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let secret: veilid_core::BareSharedSecret = + let secret: veilid_core::SharedSecret = veilid_core::deserialize_opt_json(secret.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -1717,7 +1694,7 @@ pub extern "C" fn crypto_check_shared_secret(port: i64, kind: u32, secret: FfiSt #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn crypto_check_nonce(port: i64, kind: u32, nonce: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let nonce: veilid_core::BareNonce = + let nonce: veilid_core::Nonce = veilid_core::deserialize_opt_json(nonce.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -1742,7 +1719,7 @@ pub extern "C" fn crypto_check_nonce(port: i64, kind: u32, nonce: FfiStr) { #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn crypto_check_hash_digest(port: i64, kind: u32, digest: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let digest: veilid_core::BareHashDigest = + let digest: veilid_core::HashDigest = veilid_core::deserialize_opt_json(digest.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -1767,7 +1744,7 @@ pub extern "C" fn crypto_check_hash_digest(port: i64, kind: u32, digest: FfiStr) #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn crypto_check_public_key(port: i64, kind: u32, key: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BarePublicKey = + let key: veilid_core::PublicKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -1792,7 +1769,7 @@ pub extern "C" fn crypto_check_public_key(port: i64, kind: u32, key: FfiStr) { #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn crypto_check_secret_key(port: i64, kind: u32, key: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BareSecretKey = + let key: veilid_core::SecretKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -1817,7 +1794,7 @@ pub extern "C" fn crypto_check_secret_key(port: i64, kind: u32, key: FfiStr) { #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn crypto_check_signature(port: i64, kind: u32, signature: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let signature: veilid_core::BareSignature = + let signature: veilid_core::Signature = veilid_core::deserialize_opt_json(signature.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -2034,9 +2011,9 @@ pub extern "C" fn crypto_generate_hash(port: i64, kind: u32, data: FfiStr) { pub extern "C" fn crypto_validate_key_pair(port: i64, kind: u32, key: FfiStr, secret: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BarePublicKey = + let key: veilid_core::PublicKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - let secret: veilid_core::BareSecretKey = + let secret: veilid_core::SecretKey = veilid_core::deserialize_opt_json(secret.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -2066,7 +2043,7 @@ pub extern "C" fn crypto_validate_hash(port: i64, kind: u32, data: FfiStr, hash: .decode(data.into_opt_string().unwrap().as_bytes()) .unwrap(); - let hash: veilid_core::BareHashDigest = + let hash: veilid_core::HashDigest = veilid_core::deserialize_opt_json(hash.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -2087,42 +2064,14 @@ pub extern "C" fn crypto_validate_hash(port: i64, kind: u32, data: FfiStr, hash: ); } -#[no_mangle] -#[instrument(level = "trace", target = "ffi", skip_all)] -pub extern "C" fn crypto_distance(port: i64, kind: u32, key1: FfiStr, key2: FfiStr) { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let key1: veilid_core::BareHashDigest = - veilid_core::deserialize_opt_json(key1.into_opt_string()).unwrap(); - let key2: veilid_core::BareHashDigest = - veilid_core::deserialize_opt_json(key2.into_opt_string()).unwrap(); - - DartIsolateWrapper::new(port).spawn_result_json( - async move { - let veilid_api = get_veilid_api().await?; - 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) - } - .in_current_span(), - ); -} - #[no_mangle] #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn crypto_sign(port: i64, kind: u32, key: FfiStr, secret: FfiStr, data: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BarePublicKey = + let key: veilid_core::PublicKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - let secret: veilid_core::BareSecretKey = + let secret: veilid_core::SecretKey = veilid_core::deserialize_opt_json(secret.into_opt_string()).unwrap(); let data: Vec = data_encoding::BASE64URL_NOPAD .decode(data.into_opt_string().unwrap().as_bytes()) @@ -2157,12 +2106,12 @@ pub extern "C" fn crypto_verify( ) { let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BarePublicKey = + let key: veilid_core::PublicKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); let data: Vec = data_encoding::BASE64URL_NOPAD .decode(data.into_opt_string().unwrap().as_bytes()) .unwrap(); - let signature: veilid_core::BareSignature = + let signature: veilid_core::Signature = veilid_core::deserialize_opt_json(signature.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( @@ -2199,10 +2148,10 @@ pub extern "C" fn crypto_decrypt_aead( .decode(body.into_opt_string().unwrap().as_bytes()) .unwrap(); - let nonce: veilid_core::BareNonce = + let nonce: veilid_core::Nonce = veilid_core::deserialize_opt_json(nonce.into_opt_string()).unwrap(); - let shared_secret: veilid_core::BareSharedSecret = + let shared_secret: veilid_core::SharedSecret = veilid_core::deserialize_opt_json(shared_secret.into_opt_string()).unwrap(); let associated_data: Option> = associated_data @@ -2252,10 +2201,10 @@ pub extern "C" fn crypto_encrypt_aead( .decode(body.into_opt_string().unwrap().as_bytes()) .unwrap(); - let nonce: veilid_core::BareNonce = + let nonce: veilid_core::Nonce = veilid_core::deserialize_opt_json(nonce.into_opt_string()).unwrap(); - let shared_secret: veilid_core::BareSharedSecret = + let shared_secret: veilid_core::SharedSecret = veilid_core::deserialize_opt_json(shared_secret.into_opt_string()).unwrap(); let associated_data: Option> = associated_data @@ -2304,10 +2253,10 @@ pub extern "C" fn crypto_crypt_no_auth( .decode(body.into_opt_string().unwrap().as_bytes()) .unwrap(); - let nonce: veilid_core::BareNonce = + let nonce: veilid_core::Nonce = veilid_core::deserialize_opt_json(nonce.into_opt_string()).unwrap(); - let shared_secret: veilid_core::BareSharedSecret = + let shared_secret: veilid_core::SharedSecret = veilid_core::deserialize_opt_json(shared_secret.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result( diff --git a/veilid-python/poetry_install.sh b/veilid-python/poetry_install.sh deleted file mode 100755 index 21eb50f6..00000000 --- a/veilid-python/poetry_install.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -poetry config --local virtualenvs.in-project true -export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring -poetry install diff --git a/veilid-python/tests/test_crypto.py b/veilid-python/tests/test_crypto.py index 6a142f3e..cc1f9c15 100644 --- a/veilid-python/tests/test_crypto.py +++ b/veilid-python/tests/test_crypto.py @@ -6,17 +6,17 @@ from veilid.api import CryptoSystem @pytest.mark.asyncio -async def test_best_crypto_system(api_connection: veilid.VeilidAPI): - cs: CryptoSystem = await api_connection.best_crypto_system() - async with cs: - assert await cs.default_salt_length() == 16 +async def test_valid_crypto_kinds(api_connection: veilid.VeilidAPI): + kinds = await api_connection.valid_crypto_kinds() + assert len(kinds) > 0 @pytest.mark.asyncio async def test_get_crypto_system(api_connection: veilid.VeilidAPI): - cs: CryptoSystem = await api_connection.get_crypto_system(veilid.CryptoKind.CRYPTO_KIND_VLD0) - async with cs: - assert await cs.default_salt_length() == 16 + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + assert await cs.kind() == kind @pytest.mark.asyncio @@ -31,72 +31,76 @@ async def test_get_crypto_system_invalid(api_connection: veilid.VeilidAPI): @pytest.mark.asyncio async def test_hash_and_verify_password(api_connection: veilid.VeilidAPI): - cs = await api_connection.best_crypto_system() - async with cs: - nonce = await cs.random_nonce() - salt = nonce.to_bytes() + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + nonce = await cs.random_nonce() + salt = nonce.to_bytes() - # Password match - phash = await cs.hash_password(b"abc123", salt) - assert await cs.verify_password(b"abc123", phash) + # Password match + phash = await cs.hash_password(b"abc123", salt) + assert await cs.verify_password(b"abc123", phash) - # Password mismatch - await cs.hash_password(b"abc1234", salt) - assert not await cs.verify_password(b"abc12345", phash) + # Password mismatch + await cs.hash_password(b"abc1234", salt) + assert not await cs.verify_password(b"abc12345", phash) @pytest.mark.asyncio async def test_sign_and_verify_signature(api_connection: veilid.VeilidAPI): - cs = await api_connection.best_crypto_system() - async with cs: - kp1 = await cs.generate_key_pair() - kp2 = await cs.generate_key_pair() - - # BareSignature match - sig = await cs.sign(kp1.key(), kp1.secret(), b"abc123") - assert await cs.verify(kp1.key(), b"abc123", sig) + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + kp1 = await cs.generate_key_pair() + kp2 = await cs.generate_key_pair() - # BareSignature mismatch - sig2 = await cs.sign(kp1.key(), kp1.secret(), b"abc1234") - assert await cs.verify(kp1.key(), b"abc1234", sig2) - assert not await cs.verify(kp1.key(), b"abc12345", sig2) - assert not await cs.verify(kp2.key(), b"abc1234", sig2) + # Signature match + sig = await cs.sign(kp1.key(), kp1.secret(), b"abc123") + assert await cs.verify(kp1.key(), b"abc123", sig) + + # Signature mismatch + sig2 = await cs.sign(kp1.key(), kp1.secret(), b"abc1234") + assert await cs.verify(kp1.key(), b"abc1234", sig2) + assert not await cs.verify(kp1.key(), b"abc12345", sig2) + assert not await cs.verify(kp2.key(), b"abc1234", sig2) @pytest.mark.asyncio async def test_sign_and_verify_signatures(api_connection: veilid.VeilidAPI): - cs = await api_connection.best_crypto_system() - async with cs: - kind = await cs.kind() - kp1 = await cs.generate_key_pair() - - # BareSignature match - sigs = await api_connection.generate_signatures(b"abc123", [veilid.KeyPair.from_value(kind, kp1)]) - keys = [veilid.PublicKey.from_value(kind,kp1.key())] - assert (await api_connection.verify_signatures(keys, b"abc123", sigs)) == keys + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + kind = await cs.kind() + kp1 = await cs.generate_key_pair() - # BareSignature mismatch - assert (await api_connection.verify_signatures([veilid.PublicKey.from_value(kind,kp1.key())], b"abc1234", sigs)) is None + # BareSignature match + sigs = await api_connection.generate_signatures(b"abc123", [kp1]) + keys = [kp1.key()] + assert (await api_connection.verify_signatures(keys, b"abc123", sigs)) == keys + + # BareSignature mismatch + assert (await api_connection.verify_signatures([kp1.key()], b"abc1234", sigs)) is None @pytest.mark.asyncio async def test_generate_shared_secret(api_connection: veilid.VeilidAPI): - cs = await api_connection.best_crypto_system() - async with cs: - kp1 = await cs.generate_key_pair() - kp2 = await cs.generate_key_pair() - kp3 = await cs.generate_key_pair() + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + kp1 = await cs.generate_key_pair() + kp2 = await cs.generate_key_pair() + kp3 = await cs.generate_key_pair() - ssA = await cs.generate_shared_secret(kp1.key(), kp2.secret(), b"abc123") - ssB = await cs.generate_shared_secret(kp2.key(), kp1.secret(), b"abc123") + ssA = await cs.generate_shared_secret(kp1.key(), kp2.secret(), b"abc123") + ssB = await cs.generate_shared_secret(kp2.key(), kp1.secret(), b"abc123") - assert ssA == ssB + assert ssA == ssB - ssC = await cs.generate_shared_secret(kp2.key(), kp1.secret(), b"abc1234") + ssC = await cs.generate_shared_secret(kp2.key(), kp1.secret(), b"abc1234") - assert ssA != ssC + assert ssA != ssC - ssD = await cs.generate_shared_secret(kp3.key(), kp1.secret(), b"abc123") + ssD = await cs.generate_shared_secret(kp3.key(), kp1.secret(), b"abc123") - assert ssA != ssD + assert ssA != ssD diff --git a/veilid-python/tests/test_dht.py b/veilid-python/tests/test_dht.py index 212f8a7b..60bda94d 100644 --- a/veilid-python/tests/test_dht.py +++ b/veilid-python/tests/test_dht.py @@ -51,348 +51,351 @@ async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI): async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI): rc = await api_connection.new_routing_context() async with rc: - rec = await rc.create_dht_record( - veilid.DHTSchema.dflt(1), kind=veilid.CryptoKind.CRYPTO_KIND_VLD0 - ) - await rc.close_dht_record(rec.key) - await rc.delete_dht_record(rec.key) + for kind in await api_connection.valid_crypto_kinds(): + rec = await rc.create_dht_record(kind,veilid.DHTSchema.dflt(1)) + await rc.close_dht_record(rec.key) + await rc.delete_dht_record(rec.key) @pytest.mark.asyncio async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI): rc = await api_connection.new_routing_context() async with rc: - rec = await rc.create_dht_record(veilid.DHTSchema.dflt(1)) - assert await rc.get_dht_value(rec.key, ValueSubkey(0), False) is None - await rc.close_dht_record(rec.key) - await rc.delete_dht_record(rec.key) + for kind in await api_connection.valid_crypto_kinds(): + rec = await rc.create_dht_record(kind, veilid.DHTSchema.dflt(1)) + assert await rc.get_dht_value(rec.key, ValueSubkey(0), False) is None + await rc.close_dht_record(rec.key) + await rc.delete_dht_record(rec.key) @pytest.mark.asyncio async def test_set_get_dht_value(api_connection: veilid.VeilidAPI): rc = await api_connection.new_routing_context() async with rc: - rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2)) + for kind in await api_connection.valid_crypto_kinds(): + rec = await rc.create_dht_record(kind, veilid.DHTSchema.dflt(2)) - vd = await rc.set_dht_value(rec.key, ValueSubkey(0), b"BLAH BLAH BLAH") - assert vd is None + vd = await rc.set_dht_value(rec.key, ValueSubkey(0), b"BLAH BLAH BLAH") + assert vd is None - vd2 = await rc.get_dht_value(rec.key, ValueSubkey(0), False) - assert vd2 is not None + vd2 = await rc.get_dht_value(rec.key, ValueSubkey(0), False) + assert vd2 is not None - vd3 = await rc.get_dht_value(rec.key, ValueSubkey(0), True) - assert vd3 is not None + vd3 = await rc.get_dht_value(rec.key, ValueSubkey(0), True) + assert vd3 is not None - vd4 = await rc.get_dht_value(rec.key, ValueSubkey(1), False) - assert vd4 is None + vd4 = await rc.get_dht_value(rec.key, ValueSubkey(1), False) + assert vd4 is None - #print("vd2: {}", vd2.__dict__) - #print("vd3: {}", vd3.__dict__) + #print("vd2: {}", vd2.__dict__) + #print("vd3: {}", vd3.__dict__) - assert vd2 == vd3 + assert vd2 == vd3 - await rc.close_dht_record(rec.key) - await rc.delete_dht_record(rec.key) + await rc.close_dht_record(rec.key) + await rc.delete_dht_record(rec.key) @pytest.mark.asyncio async def test_set_get_dht_value_with_owner(api_connection: veilid.VeilidAPI): rc = await api_connection.new_routing_context() async with rc: + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + owner = await cs.generate_key_pair() - cs = await api_connection.best_crypto_system() - async with cs: - owner = veilid.KeyPair.from_value(await cs.kind(), await cs.generate_key_pair()) + record_key = await rc.get_dht_record_key(veilid.DHTSchema.dflt(2), owner = owner.key()) - record_key = await rc.get_dht_record_key(veilid.DHTSchema.dflt(2), owner = owner.key()) + rec = await rc.create_dht_record(kind, veilid.DHTSchema.dflt(2), owner=owner) - rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2), owner=owner) + assert rec.key == record_key + assert rec.owner == owner.key() + assert rec.owner_secret is not None + assert rec.owner_secret == owner.secret() - assert rec.key == record_key - assert rec.owner == owner.key() - assert rec.owner_secret is not None - assert rec.owner_secret == owner.secret().value() + vd = await rc.set_dht_value(rec.key, ValueSubkey(0), b"BLAH BLAH BLAH") + assert vd is None - vd = await rc.set_dht_value(rec.key, ValueSubkey(0), b"BLAH BLAH BLAH") - assert vd is None + vd2 = await rc.get_dht_value(rec.key, ValueSubkey(0), False) + assert vd2 is not None - vd2 = await rc.get_dht_value(rec.key, ValueSubkey(0), False) - assert vd2 is not None + vd3 = await rc.get_dht_value(rec.key, ValueSubkey(0), True) + assert vd3 is not None - vd3 = await rc.get_dht_value(rec.key, ValueSubkey(0), True) - assert vd3 is not None + vd4 = await rc.get_dht_value(rec.key, ValueSubkey(1), False) + assert vd4 is None - vd4 = await rc.get_dht_value(rec.key, ValueSubkey(1), False) - assert vd4 is None + #print("vd2: {}", vd2.__dict__) + #print("vd3: {}", vd3.__dict__) - #print("vd2: {}", vd2.__dict__) - #print("vd3: {}", vd3.__dict__) + assert vd2 == vd3 - assert vd2 == vd3 - - await rc.close_dht_record(rec.key) - await rc.delete_dht_record(rec.key) + await rc.close_dht_record(rec.key) + await rc.delete_dht_record(rec.key) @pytest.mark.asyncio async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI): rc = await api_connection.new_routing_context() async with rc: - rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2)) - record_key = rec.key - owner = rec.owner - secret = rec.owner_secret - assert secret is not None - owner_keypair = rec.owner_key_pair() - assert owner_keypair != None - assert owner_keypair.key() == owner - assert owner_keypair.secret().value() == secret + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + rec = await rc.create_dht_record(kind, veilid.DHTSchema.dflt(2)) + record_key = rec.key + owner = rec.owner + secret = rec.owner_secret + assert secret is not None + owner_keypair = rec.owner_key_pair() + assert owner_keypair != None + assert owner_keypair.key() == owner + assert owner_keypair.secret() == secret - cs = await api_connection.get_crypto_system(owner.kind()) - async with cs: - assert await cs.validate_key_pair(owner.value(), secret) - other_keypair = veilid.KeyPair.from_value(await cs.kind(), await cs.generate_key_pair()) + assert await cs.validate_key_pair(owner, secret) + other_keypair = await cs.generate_key_pair() - va = b"Qwertyuiop Asdfghjkl Zxcvbnm" - vb = b"1234567890" - vc = b"!@#$%^&*()" + va = b"Qwertyuiop Asdfghjkl Zxcvbnm" + vb = b"1234567890" + vc = b"!@#$%^&*()" - # Test subkey writes - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), va) - assert vdtemp is None + # Test subkey writes + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), va) + assert vdtemp is None - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), False) - assert vdtemp is not None - assert vdtemp.data == va - assert vdtemp.seq == 0 - assert vdtemp.writer == owner + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), False) + assert vdtemp is not None + assert vdtemp.data == va + assert vdtemp.seq == 0 + assert vdtemp.writer == owner - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(0), False) - assert vdtemp is None + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(0), False) + assert vdtemp is None - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), vb) - assert vdtemp is None + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), vb) + assert vdtemp is None - await sync(rc, [rec]) + await sync(rc, [rec]) - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(0), True) - assert vdtemp is not None - assert vdtemp.data == vb + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(0), True) + assert vdtemp is not None + assert vdtemp.data == vb - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), True) - assert vdtemp is not None - assert vdtemp.data == va + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), True) + assert vdtemp is not None + assert vdtemp.data == va - # Equal value should not trigger sequence number update - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), va) - assert vdtemp is None + # Equal value should not trigger sequence number update + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), va) + assert vdtemp is None - # Different value should trigger sequence number update - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vb) - assert vdtemp is None + # Different value should trigger sequence number update + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vb) + assert vdtemp is None - # Now that we initialized some subkeys - # and verified they stored correctly - # Delete things locally and reopen and see if we can write - # with the same writer key - await sync(rc, [rec]) + # Now that we initialized some subkeys + # and verified they stored correctly + # Delete things locally and reopen and see if we can write + # with the same writer key + await sync(rc, [rec]) - await rc.close_dht_record(record_key) - await rc.delete_dht_record(record_key) + await rc.close_dht_record(record_key) + await rc.delete_dht_record(record_key) - rec = await rc.open_dht_record(record_key, rec.owner_key_pair()) - assert rec is not None - assert rec.key == record_key - assert rec.owner == owner - assert rec.owner_secret == secret - assert isinstance(rec.schema, veilid.DHTSchemaDFLT) - assert rec.schema.o_cnt == 2 + rec = await rc.open_dht_record(record_key, rec.owner_key_pair()) + assert rec is not None + assert rec.key == record_key + assert rec.owner == owner + assert rec.owner_secret == secret + assert isinstance(rec.schema, veilid.DHTSchemaDFLT) + assert rec.schema.o_cnt == 2 - # Verify subkey 1 can be set before it is get but newer is available online - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vc) - assert vdtemp is not None - assert vdtemp.data == vb - assert vdtemp.seq == 1 - assert vdtemp.writer == owner + # Verify subkey 1 can be set before it is get but newer is available online + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vc) + assert vdtemp is not None + assert vdtemp.data == vb + assert vdtemp.seq == 1 + assert vdtemp.writer == owner - # Verify subkey 1 can be set a second time and it updates because seq is newer - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vc) - assert vdtemp is None + # Verify subkey 1 can be set a second time and it updates because seq is newer + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vc) + assert vdtemp is None - await sync(rc, [rec]) + await sync(rc, [rec]) - # Verify the network got the subkey update with a refresh check - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), True) - assert vdtemp is not None - assert vdtemp.data == vc - assert vdtemp.seq == 2 - assert vdtemp.writer == owner + # Verify the network got the subkey update with a refresh check + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), True) + assert vdtemp is not None + assert vdtemp.data == vc + assert vdtemp.seq == 2 + assert vdtemp.writer == owner - # Delete things locally and reopen and see if we can write - # with a different writer key (should fail) + # Delete things locally and reopen and see if we can write + # with a different writer key (should fail) - await rc.close_dht_record(record_key) - await rc.delete_dht_record(record_key) + await rc.close_dht_record(record_key) + await rc.delete_dht_record(record_key) - rec = await rc.open_dht_record(record_key, other_keypair) - assert rec is not None - assert rec.key == record_key - assert rec.owner == owner - assert rec.owner_secret is None - assert isinstance(rec.schema, veilid.DHTSchemaDFLT) - assert rec.schema.o_cnt == 2 + rec = await rc.open_dht_record(record_key, other_keypair) + assert rec is not None + assert rec.key == record_key + assert rec.owner == owner + assert rec.owner_secret is None + assert isinstance(rec.schema, veilid.DHTSchemaDFLT) + assert rec.schema.o_cnt == 2 - # Verify subkey 1 can NOT be set because we have the wrong writer - with pytest.raises(veilid.VeilidAPIError): - await rc.set_dht_value(record_key, ValueSubkey(1), va) + # Verify subkey 1 can NOT be set because we have the wrong writer + with pytest.raises(veilid.VeilidAPIError): + await rc.set_dht_value(record_key, ValueSubkey(1), va) - # Verify subkey 0 can NOT be set because we have the wrong writer - with pytest.raises(veilid.VeilidAPIError): - await rc.set_dht_value(record_key, ValueSubkey(0), va) + # Verify subkey 0 can NOT be set because we have the wrong writer + with pytest.raises(veilid.VeilidAPIError): + await rc.set_dht_value(record_key, ValueSubkey(0), va) - # Verify subkey 0 can be set because override with the right writer - # Should have prior sequence number as its returned value because it exists online at seq 0 - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(writer=owner_keypair)) - assert vdtemp is not None - assert vdtemp.data == vb - assert vdtemp.seq == 0 - assert vdtemp.writer == owner + # Verify subkey 0 can be set because override with the right writer + # Should have prior sequence number as its returned value because it exists online at seq 0 + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(writer=owner_keypair)) + assert vdtemp is not None + assert vdtemp.data == vb + assert vdtemp.seq == 0 + assert vdtemp.writer == owner - # Should update the second time to seq 1 - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(writer=owner_keypair)) - assert vdtemp is None + # Should update the second time to seq 1 + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(writer=owner_keypair)) + assert vdtemp is None - # Clean up - await rc.close_dht_record(record_key) - await rc.delete_dht_record(record_key) + # Clean up + await rc.close_dht_record(record_key) + await rc.delete_dht_record(record_key) @pytest.mark.asyncio async def test_open_writer_dht_value_no_offline(api_connection: veilid.VeilidAPI): rc = await api_connection.new_routing_context() async with rc: - rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2)) - record_key = rec.key - owner = rec.owner - secret = rec.owner_secret - assert secret is not None - owner_keypair = rec.owner_key_pair() - assert owner_keypair != None - assert owner_keypair.key() == owner - assert owner_keypair.secret().value() == secret + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + rec = await rc.create_dht_record(kind, veilid.DHTSchema.dflt(2)) + record_key = rec.key + owner = rec.owner + secret = rec.owner_secret + assert secret is not None + owner_keypair = rec.owner_key_pair() + assert owner_keypair != None + assert owner_keypair.key() == owner + assert owner_keypair.secret() == secret - cs = await api_connection.get_crypto_system(rec.key.kind()) - async with cs: - assert await cs.validate_key_pair(owner.value(), secret) - other_keypair = veilid.KeyPair.from_value(await cs.kind(), await cs.generate_key_pair()) + assert await cs.validate_key_pair(owner, secret) + other_keypair = await cs.generate_key_pair() - va = b"Qwertyuiop Asdfghjkl Zxcvbnm" - vb = b"1234567890" - vc = b"!@#$%^&*()" + va = b"Qwertyuiop Asdfghjkl Zxcvbnm" + vb = b"1234567890" + vc = b"!@#$%^&*()" - # Test subkey writes - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), va, veilid.SetDHTValueOptions(None, False)) - assert vdtemp is None + # Test subkey writes + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), va, veilid.SetDHTValueOptions(None, False)) + assert vdtemp is None - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), False) - assert vdtemp is not None - assert vdtemp.data == va - assert vdtemp.seq == 0 - assert vdtemp.writer == owner + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), False) + assert vdtemp is not None + assert vdtemp.data == va + assert vdtemp.seq == 0 + assert vdtemp.writer == owner - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(0), False) - assert vdtemp is None + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(0), False) + assert vdtemp is None - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), vb, veilid.SetDHTValueOptions(None, False)) - assert vdtemp is None + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), vb, veilid.SetDHTValueOptions(None, False)) + assert vdtemp is None - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(0), True) - assert vdtemp is not None - assert vdtemp.data == vb + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(0), True) + assert vdtemp is not None + assert vdtemp.data == vb - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), True) - assert vdtemp is not None - assert vdtemp.data == va + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), True) + assert vdtemp is not None + assert vdtemp.data == va - # Equal value should not trigger sequence number update - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), va, veilid.SetDHTValueOptions(None, False)) - assert vdtemp is None + # Equal value should not trigger sequence number update + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), va, veilid.SetDHTValueOptions(None, False)) + assert vdtemp is None - # Different value should trigger sequence number update - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vb, veilid.SetDHTValueOptions(None, False)) - assert vdtemp is None + # Different value should trigger sequence number update + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vb, veilid.SetDHTValueOptions(None, False)) + assert vdtemp is None - # Now that we initialized some subkeys - # and verified they stored correctly - # Delete things locally and reopen and see if we can write - # with the same writer key + # Now that we initialized some subkeys + # and verified they stored correctly + # Delete things locally and reopen and see if we can write + # with the same writer key - await rc.close_dht_record(record_key) - await rc.delete_dht_record(record_key) + await rc.close_dht_record(record_key) + await rc.delete_dht_record(record_key) - rec = await rc.open_dht_record(record_key, rec.owner_key_pair()) - assert rec is not None - assert rec.key == record_key - assert rec.owner == owner - assert rec.owner_secret == secret - assert isinstance(rec.schema, veilid.DHTSchemaDFLT) - assert rec.schema.o_cnt == 2 + rec = await rc.open_dht_record(record_key, rec.owner_key_pair()) + assert rec is not None + assert rec.key == record_key + assert rec.owner == owner + assert rec.owner_secret == secret + assert isinstance(rec.schema, veilid.DHTSchemaDFLT) + assert rec.schema.o_cnt == 2 - # Verify subkey 1 can be set before it is get but newer is available online - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vc, veilid.SetDHTValueOptions(None, False)) - assert vdtemp is not None - assert vdtemp.data == vb - assert vdtemp.seq == 1 - assert vdtemp.writer == owner + # Verify subkey 1 can be set before it is get but newer is available online + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vc, veilid.SetDHTValueOptions(None, False)) + assert vdtemp is not None + assert vdtemp.data == vb + assert vdtemp.seq == 1 + assert vdtemp.writer == owner - # Verify subkey 1 can be set a second time and it updates because seq is newer - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vc, veilid.SetDHTValueOptions(None, False)) - assert vdtemp is None + # Verify subkey 1 can be set a second time and it updates because seq is newer + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(1), vc, veilid.SetDHTValueOptions(None, False)) + assert vdtemp is None - # Verify the network got the subkey update with a refresh check - vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), True) - assert vdtemp is not None - assert vdtemp.data == vc - assert vdtemp.seq == 2 - assert vdtemp.writer == owner + # Verify the network got the subkey update with a refresh check + vdtemp = await rc.get_dht_value(record_key, ValueSubkey(1), True) + assert vdtemp is not None + assert vdtemp.data == vc + assert vdtemp.seq == 2 + assert vdtemp.writer == owner - # Delete things locally and reopen and see if we can write - # with a different writer key (should fail) + # Delete things locally and reopen and see if we can write + # with a different writer key (should fail) - await rc.close_dht_record(record_key) - await rc.delete_dht_record(record_key) + await rc.close_dht_record(record_key) + await rc.delete_dht_record(record_key) - rec = await rc.open_dht_record(record_key, other_keypair) - assert rec is not None - assert rec.key == record_key - assert rec.owner == owner - assert rec.owner_secret is None - assert isinstance(rec.schema, veilid.DHTSchemaDFLT) - assert rec.schema.o_cnt == 2 + rec = await rc.open_dht_record(record_key, other_keypair) + assert rec is not None + assert rec.key == record_key + assert rec.owner == owner + assert rec.owner_secret is None + assert isinstance(rec.schema, veilid.DHTSchemaDFLT) + assert rec.schema.o_cnt == 2 - # Verify subkey 1 can NOT be set because we have the wrong writer - with pytest.raises(veilid.VeilidAPIError): - await rc.set_dht_value(record_key, ValueSubkey(1), va, veilid.SetDHTValueOptions(None, False)) + # Verify subkey 1 can NOT be set because we have the wrong writer + with pytest.raises(veilid.VeilidAPIError): + await rc.set_dht_value(record_key, ValueSubkey(1), va, veilid.SetDHTValueOptions(None, False)) - # Verify subkey 0 can NOT be set because we have the wrong writer - with pytest.raises(veilid.VeilidAPIError): - await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(None, False)) + # Verify subkey 0 can NOT be set because we have the wrong writer + with pytest.raises(veilid.VeilidAPIError): + await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(None, False)) - # Verify subkey 0 can be set because override with the right writer - # Should have prior sequence number as its returned value because it exists online at seq 0 - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(writer=owner_keypair, allow_offline=False)) - assert vdtemp is not None - assert vdtemp.data == vb - assert vdtemp.seq == 0 - assert vdtemp.writer == owner + # Verify subkey 0 can be set because override with the right writer + # Should have prior sequence number as its returned value because it exists online at seq 0 + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(writer=owner_keypair, allow_offline=False)) + assert vdtemp is not None + assert vdtemp.data == vb + assert vdtemp.seq == 0 + assert vdtemp.writer == owner - # Should update the second time to seq 1 - vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(writer=owner_keypair, allow_offline=False)) - assert vdtemp is None + # Should update the second time to seq 1 + vdtemp = await rc.set_dht_value(record_key, ValueSubkey(0), va, veilid.SetDHTValueOptions(writer=owner_keypair, allow_offline=False)) + assert vdtemp is None - # Clean up - await rc.close_dht_record(record_key) - await rc.delete_dht_record(record_key) + # Clean up + await rc.close_dht_record(record_key) + await rc.delete_dht_record(record_key) @@ -437,132 +440,133 @@ async def test_watch_dht_values(): rc0 = await api0.new_routing_context() rc1 = await api1.new_routing_context() async with rc0, rc1: + for kind in await api0.valid_crypto_kinds(): - # Server 0: Make a DHT record - rec0 = await rc0.create_dht_record(veilid.DHTSchema.dflt(10)) + # Server 0: Make a DHT record + rec0 = await rc0.create_dht_record(kind, veilid.DHTSchema.dflt(10)) - # Server 0: Set some subkey we care about - vd = await rc0.set_dht_value(rec0.key, ValueSubkey(3), b"BLAH") - assert vd is None + # Server 0: Set some subkey we care about + vd = await rc0.set_dht_value(rec0.key, ValueSubkey(3), b"BLAH") + assert vd is None - await sync(rc0, [rec0]) + await sync(rc0, [rec0]) - # Server 0: Make a watch on all the subkeys - active = await rc0.watch_dht_values(rec0.key) - assert active + # Server 0: Make a watch on all the subkeys + active = await rc0.watch_dht_values(rec0.key) + assert active - # Server 1: Open the subkey - rec1 = await rc1.open_dht_record(rec0.key, rec0.owner_key_pair()) + # Server 1: Open the subkey + rec1 = await rc1.open_dht_record(rec0.key, rec0.owner_key_pair()) - # Server 1: Now set the subkey and trigger an update - vd = await rc1.set_dht_value(rec1.key, ValueSubkey(3), b"BLAH") - assert vd is None - await sync(rc1, [rec1]) + # Server 1: Now set the subkey and trigger an update + vd = await rc1.set_dht_value(rec1.key, ValueSubkey(3), b"BLAH") + assert vd is None + await sync(rc1, [rec1]) - # Server 0: Now we should NOT get an update because the update is the same as our local copy - upd = None - try: + # Server 0: Now we should NOT get an update because the update is the same as our local copy + upd = None + try: + upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) + except asyncio.TimeoutError: + pass + assert upd is None + + # Server 1: Now set subkey and trigger an update + vd = await rc1.set_dht_value(rec1.key, ValueSubkey(3), b"BLAH BLAH") + assert vd is None + await sync(rc1, [rec1]) + + # Server 0: Wait for the update upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) - except asyncio.TimeoutError: - pass - assert upd is None - # Server 1: Now set subkey and trigger an update - vd = await rc1.set_dht_value(rec1.key, ValueSubkey(3), b"BLAH BLAH") - assert vd is None - await sync(rc1, [rec1]) + # Server 0: Verify the update came back with the first changed subkey's data + assert isinstance(upd.detail, veilid.VeilidValueChange) + assert upd.detail.key == rec0.key + assert upd.detail.count == 0xFFFFFFFE + assert upd.detail.subkeys == [(3, 3)] + assert upd.detail.value is not None + assert upd.detail.value.data == b"BLAH BLAH" - # Server 0: Wait for the update - upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) + # Server 1: Now set subkey and trigger an update + vd = await rc1.set_dht_value(rec1.key, ValueSubkey(4), b"BZORT") + assert vd is None + await sync(rc1, [rec1]) - # Server 0: Verify the update came back with the first changed subkey's data - assert isinstance(upd.detail, veilid.VeilidValueChange) - assert upd.detail.key == rec0.key - assert upd.detail.count == 0xFFFFFFFE - assert upd.detail.subkeys == [(3, 3)] - assert upd.detail.value is not None - assert upd.detail.value.data == b"BLAH BLAH" - - # Server 1: Now set subkey and trigger an update - vd = await rc1.set_dht_value(rec1.key, ValueSubkey(4), b"BZORT") - assert vd is None - await sync(rc1, [rec1]) - - # Server 0: Wait for the update - upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) - - # Server 0: Verify the update came back with the first changed subkey's data - assert isinstance(upd.detail, veilid.VeilidValueChange) - assert upd.detail.key == rec0.key - assert upd.detail.count == 0xFFFFFFFD - assert upd.detail.subkeys == [(4, 4)] - assert upd.detail.value is not None - assert upd.detail.value.data == b"BZORT" - - # Server 0: Cancel some subkeys we don't care about - active = await rc0.cancel_dht_watch(rec0.key, [(ValueSubkey(0), ValueSubkey(3))]) - assert active - - # Server 1: Now set multiple subkeys and trigger an update - vd = await asyncio.gather(*[rc1.set_dht_value(rec1.key, ValueSubkey(3), b"BLAH BLAH BLAH"), rc1.set_dht_value(rec1.key, ValueSubkey(4), b"BZORT BZORT")]) - assert vd == [None, None] - await sync(rc1, [rec1]) - - # Server 0: Wait for the update - upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) - - # Server 0: Verify only one update came back - assert isinstance(upd.detail, veilid.VeilidValueChange) - assert upd.detail.key == rec0.key - assert upd.detail.count == 0xFFFFFFFC - assert upd.detail.subkeys == [(4, 4)] - assert upd.detail.value is not None - assert upd.detail.value.data == b"BZORT BZORT" - - # Server 0: Now we should NOT get any other update - upd = None - try: + # Server 0: Wait for the update upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) - except asyncio.TimeoutError: - pass - if upd is not None: - print(f"bad update: {VeilidJSONEncoder.dumps(upd)}") - assert upd is None - # Now cancel the update - active = await rc0.cancel_dht_watch(rec0.key, [(ValueSubkey(3), ValueSubkey(9))]) - assert not active + # Server 0: Verify the update came back with the first changed subkey's data + assert isinstance(upd.detail, veilid.VeilidValueChange) + assert upd.detail.key == rec0.key + assert upd.detail.count == 0xFFFFFFFD + assert upd.detail.subkeys == [(4, 4)] + assert upd.detail.value is not None + assert upd.detail.value.data == b"BZORT" - # Server 0: Wait for the cancellation update - upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) + # Server 0: Cancel some subkeys we don't care about + active = await rc0.cancel_dht_watch(rec0.key, [(ValueSubkey(0), ValueSubkey(3))]) + assert active - # Server 0: Verify only one update came back - assert isinstance(upd.detail, veilid.VeilidValueChange) - assert upd.detail.key == rec0.key - assert upd.detail.count == 0 - assert upd.detail.subkeys == [] - assert upd.detail.value is None + # Server 1: Now set multiple subkeys and trigger an update + vd = await asyncio.gather(*[rc1.set_dht_value(rec1.key, ValueSubkey(3), b"BLAH BLAH BLAH"), rc1.set_dht_value(rec1.key, ValueSubkey(4), b"BZORT BZORT")]) + assert vd == [None, None] + await sync(rc1, [rec1]) - # Now set multiple subkeys - vd = await asyncio.gather(*[rc1.set_dht_value(rec1.key, ValueSubkey(3), b"BLAH BLAH BLAH BLAH"), rc1.set_dht_value(rec1.key, ValueSubkey(5), b"BZORT BZORT BZORT")]) - assert vd == [None, None] - await sync(rc1, [rec1]) - - # Now we should NOT get an update - upd = None - try: + # Server 0: Wait for the update upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) - except asyncio.TimeoutError: - pass - if upd is not None: - print(f"bad update: {VeilidJSONEncoder.dumps(upd)}") - assert upd is None - # Clean up - await rc1.close_dht_record(rec1.key) - await rc1.delete_dht_record(rec1.key) - await rc0.close_dht_record(rec0.key) - await rc0.delete_dht_record(rec0.key) + # Server 0: Verify only one update came back + assert isinstance(upd.detail, veilid.VeilidValueChange) + assert upd.detail.key == rec0.key + assert upd.detail.count == 0xFFFFFFFC + assert upd.detail.subkeys == [(4, 4)] + assert upd.detail.value is not None + assert upd.detail.value.data == b"BZORT BZORT" + + # Server 0: Now we should NOT get any other update + upd = None + try: + upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) + except asyncio.TimeoutError: + pass + if upd is not None: + print(f"bad update: {VeilidJSONEncoder.dumps(upd)}") + assert upd is None + + # Now cancel the update + active = await rc0.cancel_dht_watch(rec0.key, [(ValueSubkey(3), ValueSubkey(9))]) + assert not active + + # Server 0: Wait for the cancellation update + upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) + + # Server 0: Verify only one update came back + assert isinstance(upd.detail, veilid.VeilidValueChange) + assert upd.detail.key == rec0.key + assert upd.detail.count == 0 + assert upd.detail.subkeys == [] + assert upd.detail.value is None + + # Now set multiple subkeys + vd = await asyncio.gather(*[rc1.set_dht_value(rec1.key, ValueSubkey(3), b"BLAH BLAH BLAH BLAH"), rc1.set_dht_value(rec1.key, ValueSubkey(5), b"BZORT BZORT BZORT")]) + assert vd == [None, None] + await sync(rc1, [rec1]) + + # Now we should NOT get an update + upd = None + try: + upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) + except asyncio.TimeoutError: + pass + if upd is not None: + print(f"bad update: {VeilidJSONEncoder.dumps(upd)}") + assert upd is None + + # Clean up + await rc1.close_dht_record(rec1.key) + await rc1.delete_dht_record(rec1.key) + await rc0.close_dht_record(rec0.key) + await rc0.delete_dht_record(rec0.key) @pytest.mark.skipif(os.getenv("INTEGRATION") != "1", reason="integration test requires two servers running") @@ -606,127 +610,132 @@ async def test_watch_many_dht_values(): async with rc0, rc1: - COUNT = 10 - records = [] + for kind in await api0.valid_crypto_kinds(): + COUNT = 10 + records = [] - # Make and watch all records - for n in range(COUNT): - print(f"making record {n}") - # Server 0: Make a DHT record - records.append(await rc0.create_dht_record(veilid.DHTSchema.dflt(1))) + # Make and watch all records + for n in range(COUNT): + print(f"making record {n}") + # Server 0: Make a DHT record + records.append(await rc0.create_dht_record(kind, veilid.DHTSchema.dflt(1))) - # Server 0: Set some subkey we care about - vd = await rc0.set_dht_value(records[n].key, ValueSubkey(0), b"BLAH") - assert vd is None + # Server 0: Set some subkey we care about + vd = await rc0.set_dht_value(records[n].key, ValueSubkey(0), b"BLAH") + assert vd is None - # Server 0: Make a watch on all the subkeys - active = await rc0.watch_dht_values(records[n].key) - assert active + # Server 0: Make a watch on all the subkeys + active = await rc0.watch_dht_values(records[n].key) + assert active - # Open and set all records - missing_records = set() - for (n, record) in enumerate(records): - print(f"setting record {n}") + # Open and set all records + missing_records = set() + for (n, record) in enumerate(records): + print(f"setting record {n}") - # Server 1: Open the subkey - _ignore = await rc1.open_dht_record(record.key, record.owner_key_pair()) + # Server 1: Open the subkey + _ignore = await rc1.open_dht_record(record.key, record.owner_key_pair()) - # Server 1: Now set the subkey and trigger an update - vd = await rc1.set_dht_value(record.key, ValueSubkey(0), b"BLAH BLAH") - assert vd is None + # Server 1: Now set the subkey and trigger an update + vd = await rc1.set_dht_value(record.key, ValueSubkey(0), b"BLAH BLAH") + assert vd is None - missing_records.add(record.key) + missing_records.add(record.key) - # Server 0: Now we should get an update for every change - for n in range(len(records)): - print(f"waiting for change {n}") + # Server 0: Now we should get an update for every change + for n in range(len(records)): + print(f"waiting for change {n}") - # Server 0: Wait for the update - try: - upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) - assert isinstance(upd.detail, veilid.VeilidValueChange) - missing_records.remove(upd.detail.key) - except: - # Dump which records didn't get updates - for (m, record) in enumerate(records): - if record.key not in missing_records: - continue - print(f"missing update for record {m}: {record}") - info0 = await api0.debug(f"record info {record.key}") - info1 = await api1.debug(f"record info {record.key}") - print(f"from rc0: {info0}") - print(f"from rc1: {info1}") - raise + # Server 0: Wait for the update + try: + upd = await asyncio.wait_for(value_change_queue.get(), timeout=10) + assert isinstance(upd.detail, veilid.VeilidValueChange) + missing_records.remove(upd.detail.key) + except: + # Dump which records didn't get updates + for (m, record) in enumerate(records): + if record.key not in missing_records: + continue + print(f"missing update for record {m}: {record}") + info0 = await api0.debug(f"record info {record.key}") + info1 = await api1.debug(f"record info {record.key}") + print(f"from rc0: {info0}") + print(f"from rc1: {info1}") + raise - # Clean up - for record in records: - await rc1.close_dht_record(record.key) - await rc1.delete_dht_record(record.key) - await rc0.close_dht_record(record.key) - await rc0.delete_dht_record(record.key) + # Clean up + for record in records: + await rc1.close_dht_record(record.key) + await rc1.delete_dht_record(record.key) + await rc0.close_dht_record(record.key) + await rc0.delete_dht_record(record.key) @pytest.mark.asyncio async def test_inspect_dht_record(api_connection: veilid.VeilidAPI): rc = await api_connection.new_routing_context() async with rc: - rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2)) + for kind in await api_connection.valid_crypto_kinds(): + rec = await rc.create_dht_record(kind, veilid.DHTSchema.dflt(2)) - vd = await rc.set_dht_value(rec.key, ValueSubkey(0), b"BLAH BLAH BLAH") - assert vd is None + vd = await rc.set_dht_value(rec.key, ValueSubkey(0), b"BLAH BLAH BLAH") + assert vd is None - rr = await rc.inspect_dht_record(rec.key, [], veilid.DHTReportScope.LOCAL) - #print("rr: {}", rr.__dict__) - assert rr.subkeys == [(0, 1)] - assert rr.local_seqs == [0, None] - assert rr.network_seqs == [None, None] + rr = await rc.inspect_dht_record(rec.key, [], veilid.DHTReportScope.LOCAL) + #print("rr: {}", rr.__dict__) + assert rr.subkeys == [(0, 1)] + assert rr.local_seqs == [0, None] + assert rr.network_seqs == [None, None] - await sync(rc, [rec]) + await sync(rc, [rec]) - rr2 = await rc.inspect_dht_record(rec.key, [], veilid.DHTReportScope.SYNC_GET) - #print("rr2: {}", rr2.__dict__) - assert rr2.subkeys == [(0, 1)] - assert rr2.local_seqs == [0, None] - assert rr2.network_seqs == [0, None] + rr2 = await rc.inspect_dht_record(rec.key, [], veilid.DHTReportScope.SYNC_GET) + #print("rr2: {}", rr2.__dict__) + assert rr2.subkeys == [(0, 1)] + assert rr2.local_seqs == [0, None] + assert rr2.network_seqs == [0, None] - await rc.close_dht_record(rec.key) - await rc.delete_dht_record(rec.key) + await rc.close_dht_record(rec.key) + await rc.delete_dht_record(rec.key) -async def _run_test_schema_limit(api_connection: veilid.VeilidAPI, open_record: Callable[[veilid.RoutingContext, int], Awaitable[tuple[veilid.DHTRecordDescriptor, Optional[veilid.KeyPair]]]], count: int, test_data: bytes): +async def _run_test_schema_limit(api_connection: veilid.VeilidAPI, open_record: Callable[[veilid.RoutingContext, veilid.CryptoSystem, int], Awaitable[tuple[veilid.DHTRecordDescriptor, Optional[veilid.KeyPair]]]], count: int, test_data: bytes): rc = await api_connection.new_routing_context() async with rc: - (desc, writer) = await open_record(rc, count) - print(f'{desc.key} {writer}') + for kind in await api_connection.valid_crypto_kinds(): + cs = await api_connection.get_crypto_system(kind) + async with cs: + (desc, writer) = await open_record(rc, cs, count) + print(f'{desc.key} {writer}') - # write dht records on server 0 - records = [] - print(f'writing {count} subkeys') - for n in range(count): - await rc.set_dht_value(desc.key, ValueSubkey(n), test_data) - print(f' {n}') + # write dht records on server 0 + records = [] + print(f'writing {count} subkeys') + for n in range(count): + await rc.set_dht_value(desc.key, ValueSubkey(n), test_data) + print(f' {n}') - await sync(rc, [desc]) + await sync(rc, [desc]) - await rc.close_dht_record(desc.key) + await rc.close_dht_record(desc.key) - # read dht records on server 0 - print(f'reading {count} subkeys') - desc1 = await rc.open_dht_record(desc.key) - for n in range(count): - vd0 = await rc.get_dht_value(desc1.key, ValueSubkey(n)) - assert vd0 is not None - assert vd0.data == test_data - print(f' {n}') + # read dht records on server 0 + print(f'reading {count} subkeys') + desc1 = await rc.open_dht_record(desc.key) + for n in range(count): + vd0 = await rc.get_dht_value(desc1.key, ValueSubkey(n)) + assert vd0 is not None + assert vd0.data == test_data + print(f' {n}') @pytest.mark.asyncio async def test_schema_limit_dflt(api_connection: veilid.VeilidAPI): - async def open_record(rc: veilid.RoutingContext, count: int) -> tuple[veilid.DHTRecordDescriptor, Optional[veilid.KeyPair]]: + async def open_record(rc: veilid.RoutingContext, cs: veilid.CryptoSystem, count: int) -> tuple[veilid.DHTRecordDescriptor, Optional[veilid.KeyPair]]: schema = veilid.DHTSchema.dflt(count) - desc = await rc.create_dht_record(schema) + desc = await rc.create_dht_record(await cs.kind(), schema) return (desc, desc.owner_key_pair()) @@ -756,13 +765,11 @@ async def test_schema_limit_dflt(api_connection: veilid.VeilidAPI): @pytest.mark.asyncio async def test_schema_limit_smpl(api_connection: veilid.VeilidAPI): - async def open_record(rc: veilid.RoutingContext, count: int) -> tuple[veilid.DHTRecordDescriptor, veilid.KeyPair]: - cs = await api_connection.best_crypto_system() - async with cs: - writer_keypair = veilid.KeyPair.from_value(await cs.kind(), await cs.generate_key_pair()) + async def open_record(rc: veilid.RoutingContext, cs: veilid.CryptoSystem, count: int) -> tuple[veilid.DHTRecordDescriptor, veilid.KeyPair]: + writer_keypair = await cs.generate_key_pair() writer_id = await api_connection.generate_member_id(writer_keypair.key()) schema = veilid.DHTSchema.smpl(0, [veilid.DHTSchemaSMPLMember(writer_id.value(), count)]) - desc = await rc.create_dht_record(schema) + desc = await rc.create_dht_record(await cs.kind(),schema) await rc.open_dht_record(desc.key, writer_keypair) return (desc, writer_keypair) @@ -819,35 +826,35 @@ async def test_dht_integration_writer_reader(): rc0 = await api0.new_routing_context() rc1 = await api1.new_routing_context() async with rc0, rc1: + for kind in await api0.valid_crypto_kinds(): + COUNT = 100 + TEST_DATA = b"test data" - COUNT = 100 - TEST_DATA = b"test data" + # write dht records on server 0 + records = [] + schema = veilid.DHTSchema.dflt(1) + print(f'writing {COUNT} records') + for n in range(COUNT): + desc = await rc0.create_dht_record(kind, schema) + records.append(desc) + print(f' {n}: key={desc.key} owner={desc.owner_bare_key_pair()}') - # write dht records on server 0 - records = [] - schema = veilid.DHTSchema.dflt(1) - print(f'writing {COUNT} records') - for n in range(COUNT): - desc = await rc0.create_dht_record(schema) - records.append(desc) - print(f' {n}: key={desc.key} owner={desc.owner_bare_key_pair()}') + await rc0.set_dht_value(desc.key, ValueSubkey(0), TEST_DATA) - await rc0.set_dht_value(desc.key, ValueSubkey(0), TEST_DATA) + await sync(rc0, records) - await sync(rc0, records) + # read dht records on server 1 + print(f'reading {COUNT} records') + n = 0 + for desc in records: + print(f' {n}: key={desc.key} owner={desc.owner_key_pair()}') + n += 1 - # read dht records on server 1 - print(f'reading {COUNT} records') - n = 0 - for desc in records: - print(f' {n}: key={desc.key} owner={desc.owner_key_pair()}') - n += 1 - - desc1 = await rc1.open_dht_record(desc.key) - vd1 = await rc1.get_dht_value(desc1.key, ValueSubkey(0)) - assert vd1 is not None - assert vd1.data == TEST_DATA - await rc1.close_dht_record(desc1.key) + desc1 = await rc1.open_dht_record(desc.key) + vd1 = await rc1.get_dht_value(desc1.key, ValueSubkey(0)) + assert vd1 is not None + assert vd1.data == TEST_DATA + await rc1.close_dht_record(desc1.key) @@ -871,51 +878,52 @@ async def test_dht_write_read_local(): # make routing contexts rc0 = await api0.new_routing_context() async with rc0: + for kind in await api0.valid_crypto_kinds(): - # Previously COUNT was set to 500, which causes these tests to take - # 10s of minutes on slow connections or debug veilid-server builds - COUNT = 100 - TEST_DATA = b"ABCD"*1024 - TEST_DATA2 = b"ABCD"*4096 + # Previously COUNT was set to 500, which causes these tests to take + # 10s of minutes on slow connections or debug veilid-server builds + COUNT = 100 + TEST_DATA = b"ABCD"*1024 + TEST_DATA2 = b"ABCD"*4096 - # write dht records on server 0 - records = [] - schema = veilid.DHTSchema.dflt(2) - print(f'writing {COUNT} records') - for n in range(COUNT): - desc = await rc0.create_dht_record(schema) - records.append(desc) + # write dht records on server 0 + records = [] + schema = veilid.DHTSchema.dflt(2) + print(f'writing {COUNT} records') + for n in range(COUNT): + desc = await rc0.create_dht_record(kind, schema) + records.append(desc) - await rc0.set_dht_value(desc.key, ValueSubkey(0), TEST_DATA) - await rc0.set_dht_value(desc.key, ValueSubkey(1), TEST_DATA2) + await rc0.set_dht_value(desc.key, ValueSubkey(0), TEST_DATA) + await rc0.set_dht_value(desc.key, ValueSubkey(1), TEST_DATA2) - print(f' {n}: {desc.key} {desc.owner}:{desc.owner_secret}') + print(f' {n}: {desc.key} {desc.owner}:{desc.owner_secret}') - await sync(rc0, records) + await sync(rc0, records) - for desc0 in records: - await rc0.close_dht_record(desc0.key) + for desc0 in records: + await rc0.close_dht_record(desc0.key) - await api0.debug("record purge local") - await api0.debug("record purge remote") + await api0.debug("record purge local") + await api0.debug("record purge remote") - # read dht records on server 0 - print(f'reading {COUNT} records') - n = 0 - for desc0 in records: - desc1 = await rc0.open_dht_record(desc0.key) + # read dht records on server 0 + print(f'reading {COUNT} records') + n = 0 + for desc0 in records: + desc1 = await rc0.open_dht_record(desc0.key) - vd0 = await rc0.get_dht_value(desc1.key, ValueSubkey(0), force_refresh=True) - assert vd0 is not None - assert vd0.data == TEST_DATA + vd0 = await rc0.get_dht_value(desc1.key, ValueSubkey(0), force_refresh=True) + assert vd0 is not None + assert vd0.data == TEST_DATA - vd1 = await rc0.get_dht_value(desc1.key, ValueSubkey(1), force_refresh=True) - assert vd1 is not None - assert vd1.data == TEST_DATA2 - await rc0.close_dht_record(desc1.key) + vd1 = await rc0.get_dht_value(desc1.key, ValueSubkey(1), force_refresh=True) + assert vd1 is not None + assert vd1.data == TEST_DATA2 + await rc0.close_dht_record(desc1.key) - print(f' {n}') - n += 1 + print(f' {n}') + n += 1 @pytest.mark.skipif(os.getenv("STRESS") != "1", reason="stress test takes a long time") @@ -939,72 +947,74 @@ async def test_dht_write_read_full_subkeys_local(): rc0 = await api0.new_routing_context() async with rc0: - # Number of records - COUNT = 8 - # Number of subkeys per record - SUBKEY_COUNT = 32 - # BareNonce to encrypt test data - NONCE = veilid.BareNonce.from_bytes(b"A"*24) - # Secret to encrypt test data - SECRET = veilid.BareSharedSecret.from_bytes(b"A"*32) - # Max subkey size - MAX_SUBKEY_SIZE = min(32768, 1024*1024//SUBKEY_COUNT) - # MAX_SUBKEY_SIZE = 256 - - # write dht records on server 0 - records = [] - subkey_data_list = [] - schema = veilid.DHTSchema.dflt(SUBKEY_COUNT) - print(f'writing {COUNT} records with full subkeys') - init_futures = set() - for n in range(COUNT): - - # Make encrypted data that is consistent and hard to compress - subkey_data = bytes(chr(ord("A")+n)*MAX_SUBKEY_SIZE, 'ascii') - print(f"subkey_data({n}):len={len(subkey_data)}") - - cs = await api0.best_crypto_system() + for kind in await api0.valid_crypto_kinds(): + cs = await api0.get_crypto_system(kind) async with cs: - subkey_data = await cs.crypt_no_auth(subkey_data, NONCE, SECRET) - subkey_data_list.append(subkey_data) + + # Number of records + COUNT = 8 + # Number of subkeys per record + SUBKEY_COUNT = 32 + # BareNonce to encrypt test data + NONCE = veilid.Nonce.from_bytes(b"A"*await cs.nonce_length()) + # Secret to encrypt test data + SECRET = veilid.SharedSecret.from_value(await cs.kind(), veilid.BareSharedSecret.from_bytes(b"A"*await cs.shared_secret_length())) + # Max subkey size + MAX_SUBKEY_SIZE = min(32768, 1024*1024//SUBKEY_COUNT) + # MAX_SUBKEY_SIZE = 256 + + # write dht records on server 0 + records = [] + subkey_data_list = [] + schema = veilid.DHTSchema.dflt(SUBKEY_COUNT) + print(f'writing {COUNT} records with full subkeys') + init_futures = set() + for n in range(COUNT): + + # Make encrypted data that is consistent and hard to compress + subkey_data = bytes(chr(ord("A")+n)*MAX_SUBKEY_SIZE, 'ascii') + print(f"subkey_data({n}):len={len(subkey_data)}") + + subkey_data = await cs.crypt_no_auth(subkey_data, NONCE, SECRET) + subkey_data_list.append(subkey_data) - desc = await rc0.create_dht_record(schema) - records.append(desc) + desc = await rc0.create_dht_record(kind, schema) + records.append(desc) - for i in range(SUBKEY_COUNT): - init_futures.add(rc0.set_dht_value(desc.key, ValueSubkey(i), subkey_data)) + for i in range(SUBKEY_COUNT): + init_futures.add(rc0.set_dht_value(desc.key, ValueSubkey(i), subkey_data)) - print(f' {n}: {desc.key} {desc.owner}:{desc.owner_secret}') + print(f' {n}: {desc.key} {desc.owner}:{desc.owner_secret}') - # Wait for all records to synchronize, with progress bars - await sync_win(rc0, records, SUBKEY_COUNT, init_futures) + # Wait for all records to synchronize, with progress bars + await sync_win(rc0, records, SUBKEY_COUNT, init_futures) - for desc0 in records: - await rc0.close_dht_record(desc0.key) + for desc0 in records: + await rc0.close_dht_record(desc0.key) - await api0.debug("record purge local") - await api0.debug("record purge remote") + await api0.debug("record purge local") + await api0.debug("record purge remote") - # read dht records on server 0 - print(f'reading {COUNT} records') - for n, desc0 in enumerate(records): - desc1 = await rc0.open_dht_record(desc0.key) + # read dht records on server 0 + print(f'reading {COUNT} records') + for n, desc0 in enumerate(records): + desc1 = await rc0.open_dht_record(desc0.key) - for i in range(SUBKEY_COUNT): - vd0 = None - while vd0 == None: - vd0 = await rc0.get_dht_value(desc1.key, ValueSubkey(i), force_refresh=True) - if vd0 != None: - assert vd0.data == subkey_data_list[n] - break - time.sleep(1) - print(f"retrying record {n} subkey {i}") + for i in range(SUBKEY_COUNT): + vd0 = None + while vd0 == None: + vd0 = await rc0.get_dht_value(desc1.key, ValueSubkey(i), force_refresh=True) + if vd0 != None: + assert vd0.data == subkey_data_list[n] + break + time.sleep(1) + print(f"retrying record {n} subkey {i}") - await rc0.close_dht_record(desc1.key) + await rc0.close_dht_record(desc1.key) - print(f' {n}') + print(f' {n}') async def sync(rc: veilid.RoutingContext, records: list[veilid.DHTRecordDescriptor]): diff --git a/veilid-python/tests/test_routing_context.py b/veilid-python/tests/test_routing_context.py index 4d141935..98e14619 100644 --- a/veilid-python/tests/test_routing_context.py +++ b/veilid-python/tests/test_routing_context.py @@ -68,10 +68,10 @@ async def test_routing_context_app_message_loopback(): rc = await api.new_routing_context() async with rc: # make a new local private route - prl, blob = await api.new_private_route() + route_blob = await api.new_private_route() try: # import it as a remote route as well so we can send to it - prr = await api.import_remote_private_route(blob) + prr = await api.import_remote_private_route(route_blob.blob) try: # send an app message to our own private route message = b"abcd1234" @@ -91,7 +91,7 @@ async def test_routing_context_app_message_loopback(): await api.release_private_route(prr) finally: # release local private route - await api.release_private_route(prl) + await api.release_private_route(route_blob.route_id) @pytest.mark.asyncio @@ -115,10 +115,10 @@ async def test_routing_context_app_call_loopback(): rc = await api.new_routing_context() async with rc: # make a new local private route - prl, blob = await api.new_private_route() + route_blob = await api.new_private_route() try: # import it as a remote route as well so we can send to it - prr = await api.import_remote_private_route(blob) + prr = await api.import_remote_private_route(route_blob.blob) try: # send an app message to our own private route @@ -146,7 +146,7 @@ async def test_routing_context_app_call_loopback(): await api.release_private_route(prr) finally: # release local private route - await api.release_private_route(prl) + await api.release_private_route(route_blob.route_id) @pytest.mark.asyncio async def test_routing_context_app_message_loopback_big_packets(): @@ -175,10 +175,10 @@ async def test_routing_context_app_message_loopback_big_packets(): rc = await api.new_routing_context() async with rc: # make a new local private route - prl, blob = await api.new_private_route() + route_blob = await api.new_private_route() try: # import it as a remote route as well so we can send to it - prr = await api.import_remote_private_route(blob) + prr = await api.import_remote_private_route(route_blob.blob) try: # do this test 5 times for _ in range(5): @@ -203,7 +203,7 @@ async def test_routing_context_app_message_loopback_big_packets(): await api.release_private_route(prr) finally: # release local private route - await api.release_private_route(prl) + await api.release_private_route(route_blob.route_id) @pytest.mark.asyncio @@ -241,13 +241,13 @@ async def test_routing_context_app_call_loopback_big_packets(): rc = await api.new_routing_context() async with rc: # make a new local private route - prl, blob = await api.new_custom_private_route( + route_blob = await api.new_custom_private_route( [veilid.CryptoKind.CRYPTO_KIND_VLD0], veilid.Stability.RELIABLE, veilid.Sequencing.ENSURE_ORDERED) try: # import it as a remote route as well so we can send to it - prr = await api.import_remote_private_route(blob) + prr = await api.import_remote_private_route(route_blob.blob) try: # do this test 5 times for _ in range(5): @@ -261,7 +261,7 @@ async def test_routing_context_app_call_loopback_big_packets(): await api.release_private_route(prr) finally: # release local private route - await api.release_private_route(prl) + await api.release_private_route(route_blob.route_id) finally: app_call_task.cancel() @@ -288,10 +288,10 @@ async def test_routing_context_app_message_loopback_bandwidth(): rc = await api.new_routing_context() async with rc: # make a new local private route - prl, blob = await api.new_private_route() + route_blob = await api.new_private_route() try: # import it as a remote route as well so we can send to it - prr = await api.import_remote_private_route(blob) + prr = await api.import_remote_private_route(route_blob.blob) try: # do this test 10000 times message = random.randbytes(16384) @@ -307,4 +307,4 @@ async def test_routing_context_app_message_loopback_bandwidth(): await api.release_private_route(prr) finally: # release local private route - await api.release_private_route(prl) + await api.release_private_route(route_blob.route_id) diff --git a/veilid-python/veilid/api.py b/veilid-python/veilid/api.py index 6530e75e..80c0e363 100644 --- a/veilid-python/veilid/api.py +++ b/veilid-python/veilid/api.py @@ -63,7 +63,7 @@ class RoutingContext(ABC): @abstractmethod async def create_dht_record( - self, schema: types.DHTSchema, owner: Optional[types.KeyPair] = None, kind: Optional[types.CryptoKind] = None + self, kind: types.CryptoKind, schema: types.DHTSchema, owner: Optional[types.KeyPair] = None ) -> types.DHTRecordDescriptor: pass @@ -240,19 +240,19 @@ class CryptoSystem(ABC): pass @abstractmethod - async def cached_dh(self, key: types.BarePublicKey, secret: types.BareSecretKey) -> types.BareSharedSecret: + async def cached_dh(self, key: types.PublicKey, secret: types.SecretKey) -> types.SharedSecret: pass @abstractmethod async def compute_dh( - self, key: types.BarePublicKey, secret: types.BareSecretKey - ) -> types.BareSharedSecret: + self, key: types.PublicKey, secret: types.SecretKey + ) -> types.SharedSecret: pass @abstractmethod async def generate_shared_secret( - self, key: types.BarePublicKey, secret: types.BareSecretKey, domain: bytes - ) -> types.BareSharedSecret: + self, key: types.PublicKey, secret: types.SecretKey, domain: bytes + ) -> types.SharedSecret: pass @abstractmethod @@ -292,27 +292,27 @@ class CryptoSystem(ABC): pass @abstractmethod - async def check_shared_secret(self, secret: types.BareSharedSecret): + async def check_shared_secret(self, secret: types.SharedSecret): pass @abstractmethod - async def check_nonce(self, nonce: types.BareNonce): + async def check_nonce(self, nonce: types.Nonce): pass @abstractmethod - async def check_hash_digest(self, digest: types.BareHashDigest): + async def check_hash_digest(self, digest: types.HashDigest): pass @abstractmethod - async def check_public_key(self, key: types.BarePublicKey): + async def check_public_key(self, key: types.PublicKey): pass @abstractmethod - async def check_secret_key(self, key: types.BareSecretKey): + async def check_secret_key(self, key: types.SecretKey): pass @abstractmethod - async def check_signature(self, signature: types.BareSignature): + async def check_signature(self, signature: types.Signature): pass @abstractmethod @@ -324,55 +324,49 @@ class CryptoSystem(ABC): pass @abstractmethod - async def derive_shared_secret(self, password: bytes, salt: bytes) -> types.BareSharedSecret: + async def derive_shared_secret(self, password: bytes, salt: bytes) -> types.SharedSecret: pass @abstractmethod - async def random_nonce(self) -> types.BareNonce: + async def random_nonce(self) -> types.Nonce: pass @abstractmethod - async def random_shared_secret(self) -> types.BareSharedSecret: + async def random_shared_secret(self) -> types.SharedSecret: pass @abstractmethod - async def generate_key_pair(self) -> types.BareKeyPair: + async def generate_key_pair(self) -> types.KeyPair: pass @abstractmethod - async def generate_hash(self, data: bytes) -> types.BareHashDigest: + async def generate_hash(self, data: bytes) -> types.HashDigest: pass @abstractmethod - async def validate_key_pair(self, key: types.BarePublicKey, secret: types.BareSecretKey) -> bool: + async def validate_key_pair(self, key: types.PublicKey, secret: types.SecretKey) -> bool: pass @abstractmethod - async def validate_hash(self, data: bytes, hash_digest: types.BareHashDigest) -> bool: - pass - - @abstractmethod - async def distance( - self, key1: types.BareHashDigest, key2: types.BareHashDigest - ) -> types.BareHashDistance: + async def validate_hash(self, data: bytes, hash_digest: types.HashDigest) -> bool: pass @abstractmethod async def sign( - self, key: types.BarePublicKey, secret: types.BareSecretKey, data: bytes - ) -> types.BareSignature: + self, key: types.PublicKey, secret: types.SecretKey, data: bytes + ) -> types.Signature: pass @abstractmethod - async def verify(self, key: types.BarePublicKey, data: bytes, signature: types.BareSignature) -> bool: + async def verify(self, key: types.PublicKey, data: bytes, signature: types.Signature) -> bool: pass @abstractmethod async def decrypt_aead( self, body: bytes, - nonce: types.BareNonce, - shared_secret: types.BareSharedSecret, + nonce: types.Nonce, + shared_secret: types.SharedSecret, associated_data: Optional[bytes], ) -> bytes: pass @@ -381,15 +375,15 @@ class CryptoSystem(ABC): async def encrypt_aead( self, body: bytes, - nonce: types.BareNonce, - shared_secret: types.BareSharedSecret, + nonce: types.Nonce, + shared_secret: types.SharedSecret, associated_data: Optional[bytes], ) -> bytes: pass @abstractmethod async def crypt_no_auth( - self, body: bytes, nonce: types.BareNonce, shared_secret: types.BareSharedSecret + self, body: bytes, nonce: types.Nonce, shared_secret: types.SharedSecret ) -> bytes: pass @@ -440,7 +434,7 @@ class VeilidAPI(ABC): pass @abstractmethod - async def new_private_route(self) -> tuple[types.RouteId, bytes]: + async def new_private_route(self) -> types.RouteBlob: pass @abstractmethod @@ -449,7 +443,7 @@ class VeilidAPI(ABC): kinds: list[types.CryptoKind], stability: types.Stability, sequencing: types.Sequencing, - ) -> tuple[types.RouteId, bytes]: + ) -> types.RouteBlob: pass @abstractmethod @@ -480,10 +474,6 @@ class VeilidAPI(ABC): async def get_crypto_system(self, kind: types.CryptoKind) -> CryptoSystem: pass - @abstractmethod - async def best_crypto_system(self) -> CryptoSystem: - pass - @abstractmethod async def verify_signatures( self, @@ -530,3 +520,7 @@ class VeilidAPI(ABC): @abstractmethod async def default_veilid_config(self) -> str: pass + + @abstractmethod + async def valid_crypto_kinds(self) -> list[types.CryptoKind]: + pass diff --git a/veilid-python/veilid/json_api.py b/veilid-python/veilid/json_api.py index c48ac40e..cb7748e9 100644 --- a/veilid-python/veilid/json_api.py +++ b/veilid-python/veilid/json_api.py @@ -2,7 +2,6 @@ import asyncio import importlib.resources as importlib_resources import json import os -import traceback from typing import Awaitable, Callable, Optional, Self @@ -20,25 +19,24 @@ from .operations import ( ) from .state import VeilidState, VeilidUpdate from .types import ( - BareHashDistance, CryptoKind, DHTRecordDescriptor, DHTRecordReport, DHTReportScope, DHTSchema, - BareHashDigest, - BareKeyPair, - NewPrivateRouteResult, - BareNonce, + HashDigest, + KeyPair, + RouteBlob, + Nonce, OperationId, - BarePublicKey, + PublicKey, RouteId, SafetySelection, - BareSecretKey, + SecretKey, Sequencing, SetDHTValueOptions, - BareSharedSecret, - BareSignature, + SharedSecret, + Signature, MemberId, Stability, Timestamp, @@ -62,7 +60,7 @@ _STREAM_LIMIT = (65536 * 4) def _get_schema_validator(schema): cls = validators.validator_for(schema) cls.check_schema(schema) - validator = cls(schema) + validator = cls(schema) # type: ignore return validator @@ -345,21 +343,21 @@ class _JsonVeilidAPI(VeilidAPI): ) ) - async def new_private_route(self) -> tuple[RouteId, bytes]: - return NewPrivateRouteResult.from_json( + async def new_private_route(self) -> RouteBlob: + return RouteBlob.from_json( raise_api_result(await self.send_ndjson_request(Operation.NEW_PRIVATE_ROUTE)) - ).to_tuple() + ) async def new_custom_private_route( self, kinds: list[CryptoKind], stability: Stability, sequencing: Sequencing - ) -> tuple[RouteId, bytes]: + ) -> RouteBlob: assert isinstance(kinds, list) for k in kinds: assert isinstance(k, CryptoKind) assert isinstance(stability, Stability) assert isinstance(sequencing, Sequencing) - return NewPrivateRouteResult.from_json( + return RouteBlob.from_json( raise_api_result( await self.send_ndjson_request( Operation.NEW_CUSTOM_PRIVATE_ROUTE, @@ -368,7 +366,7 @@ class _JsonVeilidAPI(VeilidAPI): sequencing=sequencing, ) ) - ).to_tuple() + ) async def import_remote_private_route(self, blob: bytes) -> RouteId: assert isinstance(blob, bytes) @@ -426,10 +424,6 @@ class _JsonVeilidAPI(VeilidAPI): ) return _JsonCryptoSystem(self, cs_id) - async def best_crypto_system(self) -> CryptoSystem: - cs_id = raise_api_result(await self.send_ndjson_request(Operation.BEST_CRYPTO_SYSTEM)) - return _JsonCryptoSystem(self, cs_id) - async def verify_signatures( self, node_ids: list[PublicKey], data: bytes, signatures: list[Signature] ) -> Optional[list[PublicKey]]: @@ -508,6 +502,17 @@ class _JsonVeilidAPI(VeilidAPI): async def default_veilid_config(self) -> str: return raise_api_result(await self.send_ndjson_request(Operation.DEFAULT_VEILID_CONFIG)) + async def valid_crypto_kinds(self) -> list[CryptoKind]: + return list( + map( + lambda x: CryptoKind(x), + raise_api_result( + await self.send_ndjson_request(Operation.VALID_CRYPTO_KINDS) + ) + ) + ) + + ###################################################### @@ -665,11 +670,11 @@ class _JsonRoutingContext(RoutingContext): ) async def create_dht_record( - self, schema: DHTSchema, owner: Optional[KeyPair] = None, kind: Optional[CryptoKind] = None + self, kind: CryptoKind, schema: DHTSchema, owner: Optional[KeyPair] = None ) -> DHTRecordDescriptor: + assert isinstance(kind, CryptoKind) assert isinstance(schema, DHTSchema) assert owner is None or isinstance(owner, KeyPair) - assert kind is None or isinstance(kind, CryptoKind) return DHTRecordDescriptor.from_json( raise_api_result( @@ -1134,11 +1139,11 @@ class _JsonCryptoSystem(CryptoSystem): ) self.done = True - async def cached_dh(self, key: BarePublicKey, secret: BareSecretKey) -> BareSharedSecret: - assert isinstance(key, BarePublicKey) - assert isinstance(secret, BareSecretKey) + async def cached_dh(self, key: PublicKey, secret: SecretKey) -> SharedSecret: + assert isinstance(key, PublicKey) + assert isinstance(secret, SecretKey) - return BareSharedSecret( + return SharedSecret( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1151,11 +1156,11 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def compute_dh(self, key: BarePublicKey, secret: BareSecretKey) -> BareSharedSecret: - assert isinstance(key, BarePublicKey) - assert isinstance(secret, BareSecretKey) + async def compute_dh(self, key: PublicKey, secret: SecretKey) -> SharedSecret: + assert isinstance(key, PublicKey) + assert isinstance(secret, SecretKey) - return BareSharedSecret( + return SharedSecret( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1168,12 +1173,12 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def generate_shared_secret(self, key: BarePublicKey, secret: BareSecretKey, domain: bytes) -> BareSharedSecret: - assert isinstance(key, BarePublicKey) - assert isinstance(secret, BareSecretKey) + async def generate_shared_secret(self, key: PublicKey, secret: SecretKey, domain: bytes) -> SharedSecret: + assert isinstance(key, PublicKey) + assert isinstance(secret, SecretKey) assert isinstance(domain, bytes) - return BareSharedSecret( + return SharedSecret( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1282,8 +1287,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def check_shared_secret(self, secret: BareSharedSecret): - assert isinstance(secret, BareSharedSecret) + async def check_shared_secret(self, secret: SharedSecret): + assert isinstance(secret, SharedSecret) return raise_api_result( await self.api.send_ndjson_request( @@ -1295,8 +1300,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def check_nonce(self, nonce: BareNonce): - assert isinstance(nonce, BareNonce) + async def check_nonce(self, nonce: Nonce): + assert isinstance(nonce, Nonce) return raise_api_result( await self.api.send_ndjson_request( @@ -1308,8 +1313,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def check_hash_digest(self, digest: BareHashDigest): - assert isinstance(digest, BareHashDigest) + async def check_hash_digest(self, digest: HashDigest): + assert isinstance(digest, HashDigest) return raise_api_result( await self.api.send_ndjson_request( @@ -1321,8 +1326,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def check_public_key(self, key: BarePublicKey): - assert isinstance(key, BarePublicKey) + async def check_public_key(self, key: PublicKey): + assert isinstance(key, PublicKey) return raise_api_result( await self.api.send_ndjson_request( @@ -1334,8 +1339,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def check_secret_key(self, key: BareSecretKey): - assert isinstance(key, BareSecretKey) + async def check_secret_key(self, key: SecretKey): + assert isinstance(key, SecretKey) return raise_api_result( await self.api.send_ndjson_request( @@ -1347,8 +1352,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def check_signature(self, signature: BareSignature): - assert isinstance(signature, BareSignature) + async def check_signature(self, signature: Signature): + assert isinstance(signature, Signature) return raise_api_result( await self.api.send_ndjson_request( @@ -1390,11 +1395,11 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def derive_shared_secret(self, password: bytes, salt: bytes) -> BareSharedSecret: + async def derive_shared_secret(self, password: bytes, salt: bytes) -> SharedSecret: assert isinstance(password, bytes) assert isinstance(salt, bytes) - return BareSharedSecret( + return SharedSecret( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1407,8 +1412,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def random_nonce(self) -> BareNonce: - return BareNonce( + async def random_nonce(self) -> Nonce: + return Nonce( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1419,8 +1424,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def random_shared_secret(self) -> BareSharedSecret: - return BareSharedSecret( + async def random_shared_secret(self) -> SharedSecret: + return SharedSecret( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1431,8 +1436,8 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def generate_key_pair(self) -> BareKeyPair: - return BareKeyPair( + async def generate_key_pair(self) -> KeyPair: + return KeyPair( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1443,10 +1448,10 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def generate_hash(self, data: bytes) -> BareHashDigest: + async def generate_hash(self, data: bytes) -> HashDigest: assert isinstance(data, bytes) - return BareHashDigest( + return HashDigest( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1458,9 +1463,9 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def validate_key_pair(self, key: BarePublicKey, secret: BareSecretKey) -> bool: - assert isinstance(key, BarePublicKey) - assert isinstance(secret, BareSecretKey) + async def validate_key_pair(self, key: PublicKey, secret: SecretKey) -> bool: + assert isinstance(key, PublicKey) + assert isinstance(secret, SecretKey) return raise_api_result( await self.api.send_ndjson_request( @@ -1473,9 +1478,9 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def validate_hash(self, data: bytes, hash_digest: BareHashDigest) -> bool: + async def validate_hash(self, data: bytes, hash_digest: HashDigest) -> bool: assert isinstance(data, bytes) - assert isinstance(hash_digest, BareHashDigest) + assert isinstance(hash_digest, HashDigest) return raise_api_result( await self.api.send_ndjson_request( @@ -1488,29 +1493,12 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def distance(self, key1: BareHashDigest, key2: BareHashDigest) -> BareHashDistance: - assert isinstance(key1, BareHashDigest) - assert isinstance(key2, BareHashDigest) - - return BareHashDistance( - raise_api_result( - await self.api.send_ndjson_request( - Operation.CRYPTO_SYSTEM, - validate=validate_cs_op, - cs_id=self.cs_id, - cs_op=CryptoSystemOperation.DISTANCE, - key1=key1, - key2=key2, - ) - ) - ) - - async def sign(self, key: BarePublicKey, secret: BareSecretKey, data: bytes) -> BareSignature: - assert isinstance(key, BarePublicKey) - assert isinstance(secret, BareSecretKey) + async def sign(self, key: PublicKey, secret: SecretKey, data: bytes) -> Signature: + assert isinstance(key, PublicKey) + assert isinstance(secret, SecretKey) assert isinstance(data, bytes) - return BareSignature( + return Signature( raise_api_result( await self.api.send_ndjson_request( Operation.CRYPTO_SYSTEM, @@ -1524,10 +1512,10 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def verify(self, key: BarePublicKey, data: bytes, signature: BareSignature): - assert isinstance(key, BarePublicKey) + async def verify(self, key: PublicKey, data: bytes, signature: Signature): + assert isinstance(key, PublicKey) assert isinstance(data, bytes) - assert isinstance(signature, BareSignature) + assert isinstance(signature, Signature) return raise_api_result( await self.api.send_ndjson_request( @@ -1544,13 +1532,13 @@ class _JsonCryptoSystem(CryptoSystem): async def decrypt_aead( self, body: bytes, - nonce: BareNonce, - shared_secret: BareSharedSecret, + nonce: Nonce, + shared_secret: SharedSecret, associated_data: Optional[bytes], ) -> bytes: assert isinstance(body, bytes) - assert isinstance(nonce, BareNonce) - assert isinstance(shared_secret, BareSharedSecret) + assert isinstance(nonce, Nonce) + assert isinstance(shared_secret, SharedSecret) assert associated_data is None or isinstance(associated_data, bytes) return urlsafe_b64decode_no_pad( @@ -1571,13 +1559,13 @@ class _JsonCryptoSystem(CryptoSystem): async def encrypt_aead( self, body: bytes, - nonce: BareNonce, - shared_secret: BareSharedSecret, + nonce: Nonce, + shared_secret: SharedSecret, associated_data: Optional[bytes], ) -> bytes: assert isinstance(body, bytes) - assert isinstance(nonce, BareNonce) - assert isinstance(shared_secret, BareSharedSecret) + assert isinstance(nonce, Nonce) + assert isinstance(shared_secret, SharedSecret) assert associated_data is None or isinstance(associated_data, bytes) return urlsafe_b64decode_no_pad( @@ -1595,10 +1583,10 @@ class _JsonCryptoSystem(CryptoSystem): ) ) - async def crypt_no_auth(self, body: bytes, nonce: BareNonce, shared_secret: BareSharedSecret) -> bytes: + async def crypt_no_auth(self, body: bytes, nonce: Nonce, shared_secret: SharedSecret) -> bytes: assert isinstance(body, bytes) - assert isinstance(nonce, BareNonce) - assert isinstance(shared_secret, BareSharedSecret) + assert isinstance(nonce, Nonce) + assert isinstance(shared_secret, SharedSecret) return urlsafe_b64decode_no_pad( raise_api_result( await self.api.send_ndjson_request( diff --git a/veilid-python/veilid/operations.py b/veilid-python/veilid/operations.py index 4fa24a7e..466fe3b0 100644 --- a/veilid-python/veilid/operations.py +++ b/veilid-python/veilid/operations.py @@ -21,7 +21,6 @@ class Operation(StrEnum): TABLE_DB = "TableDb" TABLE_DB_TRANSACTION = "TableDbTransaction" GET_CRYPTO_SYSTEM = "GetCryptoSystem" - BEST_CRYPTO_SYSTEM = "BestCryptoSystem" CRYPTO_SYSTEM = "CryptoSystem" VERIFY_SIGNATURES = "VerifySignatures" GENERATE_SIGNATURES = "GenerateSignatures" @@ -32,6 +31,7 @@ class Operation(StrEnum): VEILID_VERSION = "VeilidVersion" VEILID_FEATURES = "VeilidFeatures" DEFAULT_VEILID_CONFIG = "DefaultVeilidConfig" + VALID_CRYPTO_KINDS = "ValidCryptoKinds" class RoutingContextOperation(StrEnum): diff --git a/veilid-python/veilid/schema/RecvMessage.json b/veilid-python/veilid/schema/RecvMessage.json index 390a22f2..433e651e 100644 --- a/veilid-python/veilid/schema/RecvMessage.json +++ b/veilid-python/veilid/schema/RecvMessage.json @@ -835,14 +835,34 @@ "cs_op": { "type": "string", "const": "ValidateKeyPair" - }, - "value": { - "type": "boolean" } }, + "anyOf": [ + { + "type": "object", + "properties": { + "value": { + "type": "boolean" + } + }, + "required": [ + "value" + ] + }, + { + "type": "object", + "properties": { + "error": { + "$ref": "#/$defs/VeilidAPIError" + } + }, + "required": [ + "error" + ] + } + ], "required": [ - "cs_op", - "value" + "cs_op" ] }, { @@ -851,30 +871,34 @@ "cs_op": { "type": "string", "const": "ValidateHash" - }, - "value": { - "type": "boolean" } }, - "required": [ - "cs_op", - "value" - ] - }, - { - "type": "object", - "properties": { - "cs_op": { - "type": "string", - "const": "Distance" + "anyOf": [ + { + "type": "object", + "properties": { + "value": { + "type": "boolean" + } + }, + "required": [ + "value" + ] }, - "value": { - "type": "string" + { + "type": "object", + "properties": { + "error": { + "$ref": "#/$defs/VeilidAPIError" + } + }, + "required": [ + "error" + ] } - }, + ], "required": [ - "cs_op", - "value" + "cs_op" ] }, { @@ -1271,21 +1295,6 @@ "slowest" ] }, - "NewPrivateRouteResult": { - "type": "object", - "properties": { - "blob": { - "type": "string" - }, - "route_id": { - "type": "string" - } - }, - "required": [ - "route_id", - "blob" - ] - }, "PeerStats": { "description": "Statistics for a peer in the routing table", "type": "object", @@ -1750,7 +1759,7 @@ "type": "object", "properties": { "value": { - "$ref": "#/$defs/NewPrivateRouteResult" + "$ref": "#/$defs/RouteBlob" } }, "required": [ @@ -1786,7 +1795,7 @@ "type": "object", "properties": { "value": { - "$ref": "#/$defs/NewPrivateRouteResult" + "$ref": "#/$defs/RouteBlob" } }, "required": [ @@ -2403,9 +2412,43 @@ "op", "value" ] + }, + { + "type": "object", + "properties": { + "op": { + "type": "string", + "const": "ValidCryptoKinds" + }, + "value": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "op", + "value" + ] } ] }, + "RouteBlob": { + "type": "object", + "properties": { + "blob": { + "type": "string" + }, + "route_id": { + "type": "string" + } + }, + "required": [ + "route_id", + "blob" + ] + }, "RoutingContextResponse": { "type": "object", "properties": { diff --git a/veilid-python/veilid/schema/Request.json b/veilid-python/veilid/schema/Request.json index 72504293..a29e9ee8 100644 --- a/veilid-python/veilid/schema/Request.json +++ b/veilid-python/veilid/schema/Request.json @@ -291,18 +291,6 @@ "kind" ] }, - { - "type": "object", - "properties": { - "op": { - "type": "string", - "const": "BestCryptoSystem" - } - }, - "required": [ - "op" - ] - }, { "type": "object", "properties": { @@ -460,6 +448,18 @@ "required": [ "op" ] + }, + { + "type": "object", + "properties": { + "op": { + "type": "string", + "const": "ValidCryptoKinds" + } + }, + "required": [ + "op" + ] } ], "$defs": { @@ -926,26 +926,6 @@ "hash_digest" ] }, - { - "type": "object", - "properties": { - "cs_op": { - "type": "string", - "const": "Distance" - }, - "key1": { - "type": "string" - }, - "key2": { - "type": "string" - } - }, - "required": [ - "cs_op", - "key1", - "key2" - ] - }, { "type": "object", "properties": { @@ -1347,10 +1327,7 @@ "type": "object", "properties": { "kind": { - "type": [ - "string", - "null" - ] + "type": "string" }, "owner": { "type": [ @@ -1368,6 +1345,7 @@ }, "required": [ "rc_op", + "kind", "schema" ] }, diff --git a/veilid-python/veilid/types.py b/veilid-python/veilid/types.py index 0f3de8e2..1db1b89d 100644 --- a/veilid-python/veilid/types.py +++ b/veilid-python/veilid/types.py @@ -51,6 +51,7 @@ class VeilidLogLevel(StrEnum): class CryptoKind(StrEnum): CRYPTO_KIND_NONE = "NONE" CRYPTO_KIND_VLD0 = "VLD0" + CRYPTO_KIND_VLD1 = "VLD1" class VeilidCapability(StrEnum): @@ -123,9 +124,6 @@ class EncodedString(str): assert isinstance(b, bytes) return cls(urlsafe_b64encode_no_pad(b)) -class BareHashDistance(EncodedString): - pass - class BareRecordKey(EncodedString): pass @@ -144,7 +142,7 @@ class BareHashDigest(EncodedString): class BareSignature(EncodedString): pass -class BareNonce(EncodedString): +class Nonce(EncodedString): pass @@ -193,6 +191,26 @@ class RecordKey(CryptoTyped): def value(self) -> BareRecordKey: return BareRecordKey(self._value()) +class SharedSecret(CryptoTyped): + @classmethod + def from_value(cls, kind: CryptoKind, value: BareSharedSecret) -> Self: + assert isinstance(kind, CryptoKind) + assert isinstance(value, BareSharedSecret) + return cls(f"{kind}:{value}") + + def value(self) -> BareSharedSecret: + return BareSharedSecret(self._value()) + +class HashDigest(CryptoTyped): + @classmethod + def from_value(cls, kind: CryptoKind, value: BareHashDigest) -> Self: + assert isinstance(kind, CryptoKind) + assert isinstance(value, BareHashDigest) + return cls(f"{kind}:{value}") + + def value(self) -> BareHashDigest: + return BareHashDigest(self._value()) + class PublicKey(CryptoTyped): @classmethod def from_value(cls, kind: CryptoKind, value: BarePublicKey) -> Self: @@ -328,7 +346,7 @@ class VeilidVersion: return self._patch -class NewPrivateRouteResult: +class RouteBlob: route_id: RouteId blob: bytes @@ -339,13 +357,13 @@ class NewPrivateRouteResult: self.route_id = route_id self.blob = blob - def to_tuple(self) -> tuple[RouteId, bytes]: - return (self.route_id, self.blob) - @classmethod def from_json(cls, j: dict) -> Self: return cls(RouteId(j["route_id"]), urlsafe_b64decode_no_pad(j["blob"])) + def to_json(self) -> dict: + return self.__dict__ + class DHTSchemaSMPLMember: m_key: BareMemberId @@ -445,14 +463,14 @@ class DHTSchemaSMPL(DHTSchema): class DHTRecordDescriptor: key: RecordKey owner: PublicKey - owner_secret: Optional[BareSecretKey] + owner_secret: Optional[SecretKey] schema: DHTSchema def __init__( self, key: RecordKey, owner: PublicKey, - owner_secret: Optional[BareSecretKey], + owner_secret: Optional[SecretKey], schema: DHTSchema, ): self.key = key @@ -466,19 +484,19 @@ class DHTRecordDescriptor: def owner_bare_key_pair(self) -> Optional[BareKeyPair]: if self.owner_secret is None: return None - return BareKeyPair.from_parts(self.owner.value(), self.owner_secret) + return BareKeyPair.from_parts(self.owner.value(), self.owner_secret.value()) def owner_key_pair(self) -> Optional[KeyPair]: if self.owner_secret is None: return None - return KeyPair.from_value(self.owner.kind(), BareKeyPair.from_parts(self.owner.value(), self.owner_secret)) + return KeyPair.from_value(self.owner.kind(), BareKeyPair.from_parts(self.owner.value(), self.owner_secret.value())) @classmethod def from_json(cls, j: dict) -> Self: return cls( RecordKey(j["key"]), PublicKey(j["owner"]), - None if j["owner_secret"] is None else BareSecretKey(j["owner_secret"]), + None if j["owner_secret"] is None else SecretKey(j["owner_secret"]), DHTSchema.from_json(j["schema"]), ) @@ -595,14 +613,14 @@ class ValueData: class SafetySpec: - preferred_route: Optional[BareRouteId] + preferred_route: Optional[RouteId] hop_count: int stability: Stability sequencing: Sequencing def __init__( self, - preferred_route: Optional[BareRouteId], + preferred_route: Optional[RouteId], hop_count: int, stability: Stability, sequencing: Sequencing, @@ -615,7 +633,7 @@ class SafetySpec: @classmethod def from_json(cls, j: dict) -> Self: return cls( - BareRouteId(j["preferred_route"]) if "preferred_route" in j else None, + RouteId(j["preferred_route"]) if "preferred_route" in j else None, j["hop_count"], Stability(j["stability"]), Sequencing(j["sequencing"]), diff --git a/veilid-remote-api/src/crypto_system.rs b/veilid-remote-api/src/crypto_system.rs index b2fd3b6a..d652c574 100644 --- a/veilid-remote-api/src/crypto_system.rs +++ b/veilid-remote-api/src/crypto_system.rs @@ -21,21 +21,21 @@ pub enum CryptoSystemRequestOp { Kind, CachedDh { #[schemars(with = "String")] - key: BarePublicKey, + key: PublicKey, #[schemars(with = "String")] - secret: BareSecretKey, + secret: SecretKey, }, ComputeDh { #[schemars(with = "String")] - key: BarePublicKey, + key: PublicKey, #[schemars(with = "String")] - secret: BareSecretKey, + secret: SecretKey, }, GenerateSharedSecret { #[schemars(with = "String")] - key: BarePublicKey, + key: PublicKey, #[schemars(with = "String")] - secret: BareSecretKey, + secret: SecretKey, #[serde(with = "as_human_base64")] #[schemars(with = "String")] domain: Vec, @@ -53,27 +53,27 @@ pub enum CryptoSystemRequestOp { AeadOverhead, CheckSharedSecret { #[schemars(with = "String")] - secret: BareSharedSecret, + secret: SharedSecret, }, CheckNonce { #[schemars(with = "String")] - nonce: BareNonce, + nonce: Nonce, }, CheckHashDigest { #[schemars(with = "String")] - digest: BareHashDigest, + digest: HashDigest, }, CheckPublicKey { #[schemars(with = "String")] - key: BarePublicKey, + key: PublicKey, }, CheckSecretKey { #[schemars(with = "String")] - key: BareSecretKey, + key: SecretKey, }, CheckSignature { #[schemars(with = "String")] - signature: BareSignature, + signature: Signature, }, HashPassword { #[serde(with = "as_human_base64")] @@ -107,49 +107,43 @@ pub enum CryptoSystemRequestOp { }, ValidateKeyPair { #[schemars(with = "String")] - key: BarePublicKey, + key: PublicKey, #[schemars(with = "String")] - secret: BareSecretKey, + secret: SecretKey, }, ValidateHash { #[serde(with = "as_human_base64")] #[schemars(with = "String")] data: Vec, #[schemars(with = "String")] - hash_digest: BareHashDigest, - }, - Distance { - #[schemars(with = "String")] - key1: BareHashDigest, - #[schemars(with = "String")] - key2: BareHashDigest, + hash_digest: HashDigest, }, Sign { #[schemars(with = "String")] - key: BarePublicKey, + key: PublicKey, #[schemars(with = "String")] - secret: BareSecretKey, + secret: SecretKey, #[serde(with = "as_human_base64")] #[schemars(with = "String")] data: Vec, }, Verify { #[schemars(with = "String")] - key: BarePublicKey, + key: PublicKey, #[serde(with = "as_human_base64")] #[schemars(with = "String")] data: Vec, #[schemars(with = "String")] - signature: BareSignature, + signature: Signature, }, DecryptAead { #[serde(with = "as_human_base64")] #[schemars(with = "String")] body: Vec, #[schemars(with = "String")] - nonce: BareNonce, + nonce: Nonce, #[schemars(with = "String")] - shared_secret: BareSharedSecret, + shared_secret: SharedSecret, #[serde(with = "as_human_opt_base64")] #[schemars(with = "Option")] associated_data: Option>, @@ -159,9 +153,9 @@ pub enum CryptoSystemRequestOp { #[schemars(with = "String")] body: Vec, #[schemars(with = "String")] - nonce: BareNonce, + nonce: Nonce, #[schemars(with = "String")] - shared_secret: BareSharedSecret, + shared_secret: SharedSecret, #[serde(with = "as_human_opt_base64")] #[schemars(with = "Option")] associated_data: Option>, @@ -171,9 +165,9 @@ pub enum CryptoSystemRequestOp { #[schemars(with = "String")] body: Vec, #[schemars(with = "String")] - nonce: BareNonce, + nonce: Nonce, #[schemars(with = "String")] - shared_secret: BareSharedSecret, + shared_secret: SharedSecret, }, } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -188,17 +182,17 @@ pub enum CryptoSystemResponseOp { CachedDh { #[serde(flatten)] #[schemars(with = "ApiResult")] - result: ApiResultWithString, + result: ApiResultWithString, }, ComputeDh { #[serde(flatten)] #[schemars(with = "ApiResult")] - result: ApiResultWithString, + result: ApiResultWithString, }, GenerateSharedSecret { #[serde(flatten)] #[schemars(with = "ApiResult")] - result: ApiResultWithString, + result: ApiResultWithString, }, RandomBytes { #[serde(with = "as_human_base64")] @@ -264,38 +258,36 @@ pub enum CryptoSystemResponseOp { DeriveSharedSecret { #[serde(flatten)] #[schemars(with = "ApiResult")] - result: ApiResultWithString, + result: ApiResultWithString, }, RandomNonce { #[schemars(with = "String")] - value: BareNonce, + value: Nonce, }, RandomSharedSecret { #[schemars(with = "String")] - value: BareSharedSecret, + value: SharedSecret, }, GenerateKeyPair { #[schemars(with = "String")] - value: BareKeyPair, + value: KeyPair, }, GenerateHash { #[schemars(with = "String")] - value: BareHashDigest, + value: HashDigest, }, ValidateKeyPair { - value: bool, + #[serde(flatten)] + result: ApiResult, }, ValidateHash { - value: bool, - }, - Distance { - #[schemars(with = "String")] - value: BareHashDistance, + #[serde(flatten)] + result: ApiResult, }, Sign { #[serde(flatten)] #[schemars(with = "ApiResult")] - result: ApiResultWithString, + result: ApiResultWithString, }, Verify { #[serde(flatten)] diff --git a/veilid-remote-api/src/lib.rs b/veilid-remote-api/src/lib.rs index c254e705..8ac77328 100644 --- a/veilid-remote-api/src/lib.rs +++ b/veilid-remote-api/src/lib.rs @@ -110,7 +110,6 @@ pub enum RequestOp { #[schemars(with = "String")] kind: CryptoKind, }, - BestCryptoSystem, CryptoSystem(CryptoSystemRequest), VerifySignatures { #[schemars(with = "Vec")] @@ -141,15 +140,7 @@ pub enum RequestOp { VeilidVersion, VeilidFeatures, DefaultVeilidConfig, -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -pub struct NewPrivateRouteResult { - #[schemars(with = "String")] - route_id: RouteId, - #[serde(with = "as_human_base64")] - #[schemars(with = "String")] - blob: Vec, + ValidCryptoKinds, } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -181,11 +172,11 @@ pub enum ResponseOp { }, NewPrivateRoute { #[serde(flatten)] - result: ApiResult, + result: ApiResult, }, NewCustomPrivateRoute { #[serde(flatten)] - result: ApiResult, + result: ApiResult, }, ImportRemotePrivateRoute { #[serde(flatten)] @@ -265,6 +256,10 @@ pub enum ResponseOp { VeilidFeatures { value: Vec, }, + ValidCryptoKinds { + #[schemars(with = "Vec")] + value: Vec, + }, } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] diff --git a/veilid-remote-api/src/process.rs b/veilid-remote-api/src/process.rs index 6f14020c..9494b098 100644 --- a/veilid-remote-api/src/process.rs +++ b/veilid-remote-api/src/process.rs @@ -275,13 +275,13 @@ impl JsonRequestProcessor { } } RoutingContextRequestOp::CreateDhtRecord { + kind, schema, owner, - kind, } => RoutingContextResponseOp::CreateDhtRecord { result: to_json_api_result( routing_context - .create_dht_record(schema, owner, kind) + .create_dht_record(kind, schema, owner) .await .map(Box::new), ), @@ -567,17 +567,14 @@ impl JsonRequestProcessor { }, CryptoSystemRequestOp::ValidateKeyPair { key, secret } => { CryptoSystemResponseOp::ValidateKeyPair { - value: csv.validate_keypair(&key, &secret), + result: to_json_api_result(csv.validate_keypair(&key, &secret)), } } CryptoSystemRequestOp::ValidateHash { data, hash_digest } => { CryptoSystemResponseOp::ValidateHash { - value: csv.validate_hash(&data, &hash_digest), + result: to_json_api_result(csv.validate_hash(&data, &hash_digest)), } } - CryptoSystemRequestOp::Distance { key1, key2 } => CryptoSystemResponseOp::Distance { - value: csv.distance(&key1, &key2), - }, CryptoSystemRequestOp::Sign { key, secret, data } => CryptoSystemResponseOp::Sign { result: to_json_api_result_with_string(csv.sign(&key, &secret, &data)), }, @@ -658,12 +655,7 @@ impl JsonRequestProcessor { result: to_json_api_result_with_string(self.api.generate_member_id(&writer_key)), }, RequestOp::NewPrivateRoute => ResponseOp::NewPrivateRoute { - result: to_json_api_result(self.api.new_private_route().await.map(|r| { - NewPrivateRouteResult { - route_id: r.0, - blob: r.1, - } - })), + result: to_json_api_result(self.api.new_private_route().await), }, RequestOp::NewCustomPrivateRoute { kinds, @@ -673,11 +665,7 @@ impl JsonRequestProcessor { result: to_json_api_result( self.api .new_custom_private_route(&kinds, stability, sequencing) - .await - .map(|r| NewPrivateRouteResult { - route_id: r.0, - blob: r.1, - }), + .await, ), }, RequestOp::ImportRemotePrivateRoute { blob } => ResponseOp::ImportRemotePrivateRoute { @@ -787,22 +775,6 @@ impl JsonRequestProcessor { ), } } - RequestOp::BestCryptoSystem => { - let crypto = match self.api.crypto() { - Ok(v) => v, - Err(e) => { - return Response { - id, - op: ResponseOp::BestCryptoSystem { - result: to_json_api_result(Err(e)), - }, - } - } - }; - ResponseOp::BestCryptoSystem { - result: to_json_api_result(Ok(self.add_crypto_system(crypto.best().kind()))), - } - } RequestOp::CryptoSystem(csr) => { let crypto_kind = match self.lookup_crypto_system(id, csr.cs_id) { Ok(v) => v, @@ -863,7 +835,7 @@ impl JsonRequestProcessor { result: to_json_api_result_with_vec_string(crypto.generate_signatures( &data, &key_pairs, - |k, s| Signature::new(k.kind(), s), + |_k, s| s, )), } } @@ -894,6 +866,9 @@ impl JsonRequestProcessor { RequestOp::DefaultVeilidConfig => ResponseOp::DefaultVeilidConfig { value: default_veilid_config(), }, + RequestOp::ValidCryptoKinds => ResponseOp::ValidCryptoKinds { + value: VALID_CRYPTO_KINDS.to_vec(), + }, }; Response { id, op } diff --git a/veilid-remote-api/src/routing_context.rs b/veilid-remote-api/src/routing_context.rs index dbca6d54..5bcfc097 100644 --- a/veilid-remote-api/src/routing_context.rs +++ b/veilid-remote-api/src/routing_context.rs @@ -44,11 +44,11 @@ pub enum RoutingContextRequestOp { owner: PublicKey, }, CreateDhtRecord { + #[schemars(with = "String")] + kind: CryptoKind, schema: DHTSchema, #[schemars(with = "Option")] owner: Option, - #[schemars(with = "Option")] - kind: Option, }, OpenDhtRecord { #[schemars(with = "String")] diff --git a/veilid-wasm/Cargo.toml b/veilid-wasm/Cargo.toml index fb70b137..9972611b 100644 --- a/veilid-wasm/Cargo.toml +++ b/veilid-wasm/Cargo.toml @@ -17,8 +17,15 @@ crate-type = ["cdylib", "rlib"] path = "src/lib.rs" [features] -default = ["veilid-core/default-wasm"] -crypto-test = ["veilid-core/crypto-test"] +default = ["enable-crypto-vld0", "js"] +default-dart = ["enable-crypto-vld0", "dart"] +js = [] +dart = [] +enable-crypto-vld0 = ["veilid-core/enable-crypto-vld0"] +# enable-crypto-vld1 = ["veilid-core/enable-crypto-vld1"] +enable-crypto-none = ["veilid-core/enable-crypto-none"] +crypto-test = ["enable-crypto-vld0", "enable-crypto-none"] +crypto-test-none = ["enable-crypto-none"] footgun = ["veilid-core/footgun"] [dependencies] @@ -43,8 +50,10 @@ send_wrapper = "^0" futures-util = { version = "^0" } data-encoding = { version = "^2" } gloo-utils = { version = "^0", features = ["serde"] } -tsify = { version = "0.5.5", features = ["js"] } +tsify = { version = "0.5.5", features = ["js"] } serde-wasm-bindgen = "0.6.5" +wasm-bindgen-derive = "0.3.0" +paste = "1.0.15" [dev-dependencies] wasm-bindgen-test = "^0" diff --git a/veilid-wasm/src/api_result.rs b/veilid-wasm/src/api_result.rs new file mode 100644 index 00000000..7bdf8a4d --- /dev/null +++ b/veilid-wasm/src/api_result.rs @@ -0,0 +1,2 @@ +pub type APIResult = Result; +pub const APIRESULT_UNDEFINED: APIResult<()> = APIResult::Ok(()); diff --git a/veilid-wasm/src/dart/mod.rs b/veilid-wasm/src/dart/mod.rs new file mode 100644 index 00000000..8b37967e --- /dev/null +++ b/veilid-wasm/src/dart/mod.rs @@ -0,0 +1,1740 @@ +/// Veilid WASM Bindings for Flutter/Dart +/// The Flutter/Dart bindings are in this lib.rs directly +use super::*; + +// API Singleton +lazy_static! { + static ref VEILID_API: SendWrapper>> = + SendWrapper::new(RefCell::new(None)); + static ref FILTERS: SendWrapper>> = + SendWrapper::new(RefCell::new(BTreeMap::new())); + static ref ROUTING_CONTEXTS: SendWrapper>> = + SendWrapper::new(RefCell::new(BTreeMap::new())); + static ref TABLE_DBS: SendWrapper>> = + SendWrapper::new(RefCell::new(BTreeMap::new())); + static ref TABLE_DB_TRANSACTIONS: SendWrapper>> = + SendWrapper::new(RefCell::new(BTreeMap::new())); +} + +fn get_veilid_api() -> Result { + (*VEILID_API) + .borrow() + .clone() + .ok_or(veilid_core::VeilidAPIError::NotInitialized) +} + +fn take_veilid_api() -> Result { + (**VEILID_API) + .take() + .ok_or(veilid_core::VeilidAPIError::NotInitialized) +} + +// Utility types for async API results +pub fn wrap_api_future_json(future: F) -> Promise +where + F: Future> + 'static, + T: Serialize + Debug + 'static, +{ + future_to_promise(future.map(|res| res.map(|v| to_json(v)).map_err(to_json))) +} + +pub fn wrap_api_future_plain(future: F) -> Promise +where + F: Future> + 'static, + JsValue: From, + T: 'static, +{ + future_to_promise(future.map(|res| res.map(|v| to_jsvalue(v)).map_err(to_json))) +} + +pub fn wrap_api_future_void(future: F) -> Promise +where + F: Future> + 'static, +{ + future_to_promise(future.map(|res| res.map(|_| JsValue::UNDEFINED).map_err(to_json))) +} + +// WASM Bindings + +#[wasm_bindgen()] +pub fn initialize_veilid_wasm() { + console_error_panic_hook::set_once(); +} + +static INITIALIZED: AtomicBool = AtomicBool::new(false); +#[wasm_bindgen()] +pub fn initialize_veilid_core(platform_config: String) { + if INITIALIZED.swap(true, Ordering::AcqRel) { + return; + } + let platform_config: VeilidWASMConfig = veilid_core::deserialize_json(&platform_config) + .expect("failed to deserialize platform config json"); + + // Set up subscriber and layers + let subscriber = Registry::default(); + let mut layers = Vec::new(); + let mut filters = (*FILTERS).borrow_mut(); + + // Performance logger + if platform_config.logging.performance.enabled { + let filter = veilid_core::VeilidLayerFilter::new( + platform_config.logging.performance.level, + &platform_config.logging.performance.ignore_log_targets, + None, + ); + let layer = WASMLayer::new( + WASMLayerConfig::new() + .with_report_logs_in_timings(platform_config.logging.performance.logs_in_timings) + .with_console_config(if platform_config.logging.performance.logs_in_console { + ConsoleConfig::ReportWithConsoleColor + } else { + ConsoleConfig::NoReporting + }) + .with_field_filter(Some(Arc::new(|k| k != veilid_core::VEILID_LOG_KEY_FIELD))), + ) + .with_filter(filter.clone()); + filters.insert("performance", filter); + layers.push(layer.boxed()); + }; + + // API logger + if platform_config.logging.api.enabled { + let filter = veilid_core::VeilidLayerFilter::new( + platform_config.logging.api.level, + &platform_config.logging.api.ignore_log_targets, + None, + ); + let layer = veilid_core::ApiTracingLayer::init().with_filter(filter.clone()); + filters.insert("api", filter); + layers.push(layer.boxed()); + } + + let subscriber = subscriber.with(layers); + subscriber + .try_init() + .map_err(|e| format!("failed to initialize logging: {}", e)) + .expect("failed to initalize WASM platform"); +} + +#[wasm_bindgen()] +pub fn change_log_level(layer: String, log_level: String) { + let layer = if layer == "all" { "".to_owned() } else { layer }; + let Ok(log_level) = deserialize_json::(&log_level) else { + return; + }; + let filters = (*FILTERS).borrow(); + if layer.is_empty() { + // Change all layers + for f in filters.values() { + f.set_max_level(log_level); + } + } else { + // Change a specific layer + if let Some(f) = filters.get(layer.as_str()) { + f.set_max_level(log_level); + } + } +} + +#[wasm_bindgen()] +pub fn change_log_ignore(layer: String, log_ignore: String) { + let layer = if layer == "all" { "".to_owned() } else { layer }; + + let filters = (*FILTERS).borrow(); + if layer.is_empty() { + // Change all layers + for f in filters.values() { + f.set_ignore_list(Some(VeilidLayerFilter::apply_ignore_change( + &f.ignore_list(), + log_ignore.clone(), + ))); + } + } else { + // Change a specific layer + if let Some(f) = filters.get(layer.as_str()) { + f.set_ignore_list(Some(VeilidLayerFilter::apply_ignore_change( + &f.ignore_list(), + log_ignore.clone(), + ))); + } + } +} + +#[wasm_bindgen()] +pub fn startup_veilid_core(update_callback_js: Function, json_config: String) -> Promise { + let update_callback_js = SendWrapper::new(update_callback_js); + wrap_api_future_void(async move { + let update_callback = Arc::new(move |update: VeilidUpdate| { + let _ret = + match Function::call1(&update_callback_js, &JsValue::UNDEFINED, &to_json(update)) { + Ok(v) => v, + Err(e) => { + console_log(&format!("calling update callback failed: {:?}", e)); + return; + } + }; + }); + + if VEILID_API.borrow().is_some() { + return Err(veilid_core::VeilidAPIError::AlreadyInitialized); + } + + let veilid_api = veilid_core::api_startup_json(update_callback, json_config).await?; + VEILID_API.replace(Some(veilid_api)); + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn get_veilid_state() -> Promise { + wrap_api_future_json(async move { + let veilid_api = get_veilid_api()?; + let core_state = veilid_api.get_state().await?; + APIResult::Ok(core_state) + }) +} + +#[wasm_bindgen()] +pub fn is_shutdown() -> APIResult { + let veilid_api = get_veilid_api(); + if let Err(veilid_core::VeilidAPIError::NotInitialized) = veilid_api { + return APIResult::Ok(true); + } + let veilid_api = veilid_api.unwrap(); + let is_shutdown = veilid_api.is_shutdown(); + APIResult::Ok(is_shutdown) +} + +#[wasm_bindgen()] +pub fn attach() -> Promise { + wrap_api_future_void(async move { + let veilid_api = get_veilid_api()?; + veilid_api.attach().await?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn detach() -> Promise { + wrap_api_future_void(async move { + let veilid_api = get_veilid_api()?; + veilid_api.detach().await?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn shutdown_veilid_core() -> Promise { + wrap_api_future_void(async move { + let veilid_api = take_veilid_api()?; + veilid_api.shutdown().await; + APIRESULT_UNDEFINED + }) +} + +fn add_routing_context(routing_context: veilid_core::RoutingContext) -> u32 { + let mut next_id: u32 = 1; + let mut rc = (*ROUTING_CONTEXTS).borrow_mut(); + while rc.contains_key(&next_id) { + next_id += 1; + } + rc.insert(next_id, routing_context); + next_id +} + +#[wasm_bindgen()] +pub fn routing_context() -> Promise { + wrap_api_future_plain(async move { + let veilid_api = get_veilid_api()?; + let routing_context = veilid_api.routing_context()?; + let new_id = add_routing_context(routing_context); + APIResult::Ok(new_id) + }) +} + +#[wasm_bindgen()] +#[must_use] +pub fn release_routing_context(id: u32) -> i32 { + let mut rc = (*ROUTING_CONTEXTS).borrow_mut(); + if rc.remove(&id).is_none() { + return 0; + } + 1 +} + +#[wasm_bindgen()] +#[must_use] +pub fn routing_context_with_default_safety(id: u32) -> u32 { + let routing_context = { + let rc = (*ROUTING_CONTEXTS).borrow(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + routing_context.clone() + }; + let Ok(routing_context) = routing_context.with_default_safety() else { + return 0; + }; + add_routing_context(routing_context) +} + +#[wasm_bindgen()] +#[must_use] +pub fn routing_context_with_safety(id: u32, safety_selection: String) -> u32 { + let safety_selection: veilid_core::SafetySelection = + veilid_core::deserialize_json(&safety_selection).unwrap(); + + let routing_context = { + let rc = (*ROUTING_CONTEXTS).borrow(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + routing_context.clone() + }; + let Ok(routing_context) = routing_context.with_safety(safety_selection) else { + return 0; + }; + add_routing_context(routing_context) +} + +#[wasm_bindgen()] +#[must_use] +pub fn routing_context_with_sequencing(id: u32, sequencing: String) -> u32 { + let sequencing: veilid_core::Sequencing = veilid_core::deserialize_json(&sequencing).unwrap(); + + let routing_context = { + let rc = (*ROUTING_CONTEXTS).borrow(); + let Some(routing_context) = rc.get(&id) else { + return 0; + }; + routing_context.clone() + }; + let routing_context = routing_context.with_sequencing(sequencing); + add_routing_context(routing_context) +} + +fn get_routing_context(id: u32, func_name: &str) -> APIResult { + let rc = (*ROUTING_CONTEXTS).borrow(); + let Some(routing_context) = rc.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + func_name, "id", id, + )); + }; + Ok(routing_context.clone()) +} + +#[wasm_bindgen()] +pub fn routing_context_safety(id: u32) -> Promise { + wrap_api_future_json(async move { + let routing_context = get_routing_context(id, "routing_context_safety")?; + + let safety_selection = routing_context.safety(); + APIResult::Ok(safety_selection) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_app_call(id: u32, target: String, request: String) -> Promise { + wrap_api_future_plain(async move { + let target: veilid_core::Target = + veilid_core::deserialize_json(&target).map_err(VeilidAPIError::generic)?; + + let request: Vec = data_encoding::BASE64URL_NOPAD + .decode(request.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let routing_context = get_routing_context(id, "routing_context_app_call")?; + + let answer = routing_context.app_call(target, request).await?; + let answer = data_encoding::BASE64URL_NOPAD.encode(&answer); + APIResult::Ok(answer) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_app_message(id: u32, target: String, message: String) -> Promise { + wrap_api_future_void(async move { + let target: veilid_core::Target = + veilid_core::deserialize_json(&target).map_err(VeilidAPIError::generic)?; + + let message: Vec = data_encoding::BASE64URL_NOPAD + .decode(message.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let routing_context = get_routing_context(id, "routing_context_app_message")?; + + routing_context.app_message(target, message).await?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn routing_context_get_dht_record_key(id: u32, schema: String, owner: String) -> Promise { + wrap_api_future_json(async move { + let schema: veilid_core::DHTSchema = + veilid_core::deserialize_json(&schema).map_err(VeilidAPIError::generic)?; + let owner: veilid_core::PublicKey = + veilid_core::deserialize_json(&owner).map_err(VeilidAPIError::generic)?; + + let routing_context = get_routing_context(id, "routing_context_get_dht_record_key")?; + + let key = routing_context.get_dht_record_key(schema, &owner)?; + + APIResult::Ok(key) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_create_dht_record( + id: u32, + kind: u32, + schema: String, + owner: Option, +) -> Promise { + wrap_api_future_json(async move { + let crypto_kind = veilid_core::CryptoKind::from(kind); + let owner: Option = match owner { + Some(s) => Some(veilid_core::deserialize_json(&s).map_err(VeilidAPIError::generic)?), + None => None, + }; + let schema: veilid_core::DHTSchema = + veilid_core::deserialize_json(&schema).map_err(VeilidAPIError::generic)?; + + let routing_context = get_routing_context(id, "routing_context_create_dht_record")?; + + let dht_record_descriptor = routing_context + .create_dht_record(crypto_kind, schema, owner) + .await?; + APIResult::Ok(dht_record_descriptor) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_open_dht_record(id: u32, key: String, writer: Option) -> Promise { + wrap_api_future_json(async move { + let key: veilid_core::RecordKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let writer: Option = match writer { + Some(s) => Some(veilid_core::deserialize_json(&s).map_err(VeilidAPIError::generic)?), + None => None, + }; + + let routing_context = get_routing_context(id, "routing_context_open_dht_record")?; + + let dht_record_descriptor = routing_context.open_dht_record(key, writer).await?; + APIResult::Ok(dht_record_descriptor) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_close_dht_record(id: u32, key: String) -> Promise { + wrap_api_future_void(async move { + let key: veilid_core::RecordKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + + let routing_context = get_routing_context(id, "routing_context_close_dht_record")?; + + routing_context.close_dht_record(key).await?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn routing_context_delete_dht_record(id: u32, key: String) -> Promise { + wrap_api_future_void(async move { + let key: veilid_core::RecordKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + + let routing_context = get_routing_context(id, "routing_context_delete_dht_record")?; + + routing_context.delete_dht_record(key).await?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn routing_context_get_dht_value( + id: u32, + key: String, + subkey: u32, + force_refresh: bool, +) -> Promise { + wrap_api_future_json(async move { + let key: veilid_core::RecordKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + + let routing_context = get_routing_context(id, "routing_context_get_dht_value")?; + + let res = routing_context + .get_dht_value(key, subkey, force_refresh) + .await?; + APIResult::Ok(res) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_set_dht_value( + id: u32, + key: String, + subkey: u32, + data: String, + options: Option, +) -> Promise { + wrap_api_future_json(async move { + let key: veilid_core::RecordKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let options: Option = match options { + Some(s) => Some(veilid_core::deserialize_json(&s).map_err(VeilidAPIError::generic)?), + None => None, + }; + + let routing_context = get_routing_context(id, "routing_context_set_dht_value")?; + + let res = routing_context + .set_dht_value(key, subkey, data, options) + .await?; + APIResult::Ok(res) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_watch_dht_values( + id: u32, + key: String, + subkeys: String, + expiration: String, + count: u32, +) -> Promise { + wrap_api_future_plain(async move { + let key: veilid_core::RecordKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let subkeys: veilid_core::ValueSubkeyRangeSet = + veilid_core::deserialize_json(&subkeys).map_err(VeilidAPIError::generic)?; + let expiration = veilid_core::Timestamp::new( + u64::from_str(&expiration).map_err(VeilidAPIError::generic)?, + ); + + let routing_context = get_routing_context(id, "routing_context_watch_dht_values")?; + + let res = routing_context + .watch_dht_values(key, Some(subkeys), Some(expiration), Some(count)) + .await?; + APIResult::Ok(res) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_cancel_dht_watch(id: u32, key: String, subkeys: String) -> Promise { + wrap_api_future_plain(async move { + let key: veilid_core::RecordKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let subkeys: veilid_core::ValueSubkeyRangeSet = + veilid_core::deserialize_json(&subkeys).map_err(VeilidAPIError::generic)?; + + let routing_context = get_routing_context(id, "routing_context_cancel_dht_watch")?; + + let res = routing_context.cancel_dht_watch(key, Some(subkeys)).await?; + APIResult::Ok(res) + }) +} + +#[wasm_bindgen()] +pub fn routing_context_inspect_dht_record( + id: u32, + key: String, + subkeys: String, + scope: String, +) -> Promise { + wrap_api_future_json(async move { + let key: veilid_core::RecordKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let subkeys: veilid_core::ValueSubkeyRangeSet = + veilid_core::deserialize_json(&subkeys).map_err(VeilidAPIError::generic)?; + let scope: veilid_core::DHTReportScope = + veilid_core::deserialize_json(&scope).map_err(VeilidAPIError::generic)?; + + let routing_context = get_routing_context(id, "routing_context_inspect_dht_record")?; + + let res = routing_context + .inspect_dht_record(key, Some(subkeys), scope) + .await?; + + APIResult::Ok(res) + }) +} + +#[wasm_bindgen()] +pub fn generate_member_id(writer_key: String) -> Promise { + wrap_api_future_json(async move { + let writer_key: veilid_core::PublicKey = + veilid_core::deserialize_json(&writer_key).map_err(VeilidAPIError::generic)?; + + let veilid_api = get_veilid_api()?; + + let member_id = veilid_api.generate_member_id(&writer_key)?; + + APIResult::Ok(member_id) + }) +} + +#[wasm_bindgen()] +pub fn new_private_route() -> Promise { + wrap_api_future_json(async move { + let veilid_api = get_veilid_api()?; + + let route_blob = veilid_api.new_private_route().await?; + + APIResult::Ok(route_blob) + }) +} + +#[wasm_bindgen()] +pub fn new_custom_private_route(stability: String, sequencing: String) -> Promise { + wrap_api_future_json(async move { + let stability: veilid_core::Stability = + veilid_core::deserialize_json(&stability).map_err(VeilidAPIError::generic)?; + let sequencing: veilid_core::Sequencing = + veilid_core::deserialize_json(&sequencing).map_err(VeilidAPIError::generic)?; + + let veilid_api = get_veilid_api()?; + + let route_blob = veilid_api + .new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing) + .await?; + + APIResult::Ok(route_blob) + }) +} + +#[wasm_bindgen()] +pub fn import_remote_private_route(blob: String) -> Promise { + wrap_api_future_json(async move { + let blob: Vec = data_encoding::BASE64URL_NOPAD + .decode(blob.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let veilid_api = get_veilid_api()?; + + let key = veilid_api.import_remote_private_route(blob)?; + + APIResult::Ok(key) + }) +} + +#[wasm_bindgen()] +pub fn release_private_route(route_id: String) -> Promise { + wrap_api_future_void(async move { + let route_id: veilid_core::RouteId = + veilid_core::deserialize_json(&route_id).map_err(VeilidAPIError::generic)?; + let veilid_api = get_veilid_api()?; + veilid_api.release_private_route(route_id)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn app_call_reply(call_id: String, message: String) -> Promise { + wrap_api_future_void(async move { + let message: Vec = data_encoding::BASE64URL_NOPAD + .decode(message.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let call_id = match call_id.parse() { + Ok(v) => v, + Err(e) => { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + e, "call_id", call_id, + )) + } + }; + let veilid_api = get_veilid_api()?; + veilid_api.app_call_reply(call_id, message).await?; + APIRESULT_UNDEFINED + }) +} + +fn add_table_db(table_db: veilid_core::TableDB) -> u32 { + let mut next_id: u32 = 1; + let mut tdbs = (*TABLE_DBS).borrow_mut(); + while tdbs.contains_key(&next_id) { + next_id += 1; + } + tdbs.insert(next_id, table_db); + next_id +} + +#[wasm_bindgen()] +pub fn open_table_db(name: String, column_count: u32) -> Promise { + wrap_api_future_plain(async move { + let veilid_api = get_veilid_api()?; + let tstore = veilid_api.table_store()?; + let table_db = tstore + .open(&name, column_count) + .await + .map_err(veilid_core::VeilidAPIError::generic)?; + let new_id = add_table_db(table_db); + APIResult::Ok(new_id) + }) +} + +#[wasm_bindgen()] +#[must_use] +pub fn release_table_db(id: u32) -> i32 { + let mut tdbs = (*TABLE_DBS).borrow_mut(); + if tdbs.remove(&id).is_none() { + return 0; + } + 1 +} + +#[wasm_bindgen()] +pub fn delete_table_db(name: String) -> Promise { + wrap_api_future_plain(async move { + let veilid_api = get_veilid_api()?; + let tstore = veilid_api.table_store()?; + let deleted = tstore + .delete(&name) + .await + .map_err(veilid_core::VeilidAPIError::generic)?; + APIResult::Ok(deleted) + }) +} + +#[wasm_bindgen()] +#[must_use] +pub fn table_db_get_column_count(id: u32) -> u32 { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return 0; + }; + let Ok(cc) = table_db.clone().get_column_count() else { + return 0; + }; + cc +} + +fn get_table_db(id: u32, func_name: &str) -> APIResult { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + func_name, "id", id, + )); + }; + Ok(table_db.clone()) +} + +#[wasm_bindgen()] +pub fn table_db_get_keys(id: u32, col: u32) -> Promise { + wrap_api_future_json(async move { + let table_db = get_table_db(id, "table_db_get_keys")?; + + let keys = table_db.clone().get_keys(col).await?; + let out: Vec = keys + .into_iter() + .map(|k| data_encoding::BASE64URL_NOPAD.encode(&k)) + .collect(); + APIResult::Ok(out) + }) +} + +fn add_table_db_transaction(tdbt: veilid_core::TableDBTransaction) -> u32 { + let mut next_id: u32 = 1; + let mut tdbts = (*TABLE_DB_TRANSACTIONS).borrow_mut(); + while tdbts.contains_key(&next_id) { + next_id += 1; + } + tdbts.insert(next_id, tdbt); + next_id +} + +#[wasm_bindgen()] +#[must_use] +pub fn table_db_transact(id: u32) -> u32 { + let table_dbs = (*TABLE_DBS).borrow(); + let Some(table_db) = table_dbs.get(&id) else { + return 0; + }; + let tdbt = table_db.clone().transact(); + add_table_db_transaction(tdbt) +} + +#[wasm_bindgen()] +#[must_use] +pub fn release_table_db_transaction(id: u32) -> i32 { + let mut tdbts = (*TABLE_DB_TRANSACTIONS).borrow_mut(); + if tdbts.remove(&id).is_none() { + return 0; + } + 1 +} + +fn get_table_db_transaction( + id: u32, + func_name: &str, +) -> APIResult { + let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); + let Some(tdbt) = tdbts.get(&id) else { + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + func_name, "id", id, + )); + }; + Ok(tdbt.clone()) +} + +#[wasm_bindgen()] +pub fn table_db_transaction_commit(id: u32) -> Promise { + wrap_api_future_void(async move { + let tdbt = get_table_db_transaction(id, "table_db_transaction_commit")?; + + tdbt.commit().await?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_transaction_rollback(id: u32) -> Promise { + wrap_api_future_void(async move { + let tdbt = get_table_db_transaction(id, "table_db_transaction_rollback")?; + + tdbt.rollback(); + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_transaction_store(id: u32, col: u32, key: String, value: String) -> Promise { + wrap_api_future_void(async move { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let value: Vec = data_encoding::BASE64URL_NOPAD + .decode(value.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let tdbt = get_table_db_transaction(id, "table_db_transaction_store")?; + + tdbt.store(col, &key, &value)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_transaction_delete(id: u32, col: u32, key: String) -> Promise { + wrap_api_future_void(async move { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let tdbt = get_table_db_transaction(id, "table_db_transaction_delete")?; + + tdbt.delete(col, &key)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_store(id: u32, col: u32, key: String, value: String) -> Promise { + wrap_api_future_void(async move { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let value: Vec = data_encoding::BASE64URL_NOPAD + .decode(value.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let table_db = get_table_db(id, "table_db_store")?; + + table_db.store(col, &key, &value).await?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn table_db_load(id: u32, col: u32, key: String) -> Promise { + wrap_api_future_plain(async move { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let table_db = get_table_db(id, "table_db_load")?; + + let out = table_db.load(col, &key).await?; + let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x)); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn table_db_delete(id: u32, col: u32, key: String) -> Promise { + wrap_api_future_plain(async move { + let key: Vec = data_encoding::BASE64URL_NOPAD + .decode(key.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let table_db = get_table_db(id, "table_db_delete")?; + + let out = table_db.delete(col, &key).await?; + let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x)); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +#[must_use] +pub fn valid_crypto_kinds() -> String { + veilid_core::serialize_json( + veilid_core::VALID_CRYPTO_KINDS + .iter() + .map(|k| (*k).into()) + .collect::>(), + ) +} + +#[wasm_bindgen()] +pub fn verify_signatures(node_ids: String, data: String, signatures: String) -> Promise { + wrap_api_future_json(async move { + let node_ids: Vec = + veilid_core::deserialize_json(&node_ids).map_err(VeilidAPIError::generic)?; + + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let typed_signatures: Vec = + veilid_core::deserialize_json(&signatures).map_err(VeilidAPIError::generic)?; + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let out = crypto.verify_signatures(&node_ids, &data, &typed_signatures)?; + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn generate_signatures(data: String, key_pairs: String) -> Promise { + wrap_api_future_json(async move { + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let key_pairs: Vec = + veilid_core::deserialize_json(&key_pairs).map_err(VeilidAPIError::generic)?; + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let out = crypto.generate_signatures(&data, &key_pairs, |_k, s| s)?; + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn generate_key_pair(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_json(async move { + let out = veilid_core::Crypto::generate_keypair(kind)?; + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_cached_dh(kind: u32, key: String, secret: String) -> Promise { + wrap_api_future_json(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let key: veilid_core::PublicKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let secret: veilid_core::SecretKey = + veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_compute_dh(kind: u32, key: String, secret: String) -> Promise { + wrap_api_future_json(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let key: veilid_core::PublicKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let secret: veilid_core::SecretKey = + veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_generate_shared_secret( + kind: u32, + key: String, + secret: String, + domain: String, +) -> Promise { + wrap_api_future_json(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let key: veilid_core::PublicKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let secret: veilid_core::SecretKey = + veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; + let domain: Vec = data_encoding::BASE64URL_NOPAD + .decode(domain.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + 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_shared_secret", + "kind", + kind.to_string(), + ) + })?; + let out = csv.generate_shared_secret(&key, &secret, &domain)?; + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_random_bytes(kind: u32, len: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_plain(async move { + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_shared_secret_length(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_plain(async move { + 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_shared_secret_length", + "kind", + kind.to_string(), + ) + })?; + let out = csv.shared_secret_length(); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_nonce_length(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_plain(async move { + 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_nonce_length", + "kind", + kind.to_string(), + ) + })?; + let out = csv.nonce_length(); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_hash_digest_length(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_plain(async move { + 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_digest_length", + "kind", + kind.to_string(), + ) + })?; + let out = csv.hash_digest_length(); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_public_key_length(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_plain(async move { + 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_public_key_length", + "kind", + kind.to_string(), + ) + })?; + let out = csv.public_key_length(); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_secret_key_length(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_plain(async move { + 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_secret_key_length", + "kind", + kind.to_string(), + ) + })?; + let out = csv.secret_key_length(); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_signature_length(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_plain(async move { + 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_signature_length", + "kind", + kind.to_string(), + ) + })?; + let out = csv.signature_length(); + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_default_salt_length(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_plain(async move { + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_aead_overhead(kind: u32) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_check_shared_secret(kind: u32, secret: String) -> Promise { + wrap_api_future_void(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let secret: veilid_core::SharedSecret = + veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; + + 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_check_shared_secret", + "kind", + kind.to_string(), + ) + })?; + csv.check_shared_secret(&secret)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn crypto_check_nonce(kind: u32, nonce: String) -> Promise { + wrap_api_future_void(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let nonce: veilid_core::Nonce = + veilid_core::deserialize_json(&nonce).map_err(VeilidAPIError::generic)?; + + 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_check_nonce", + "kind", + kind.to_string(), + ) + })?; + csv.check_nonce(&nonce)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn crypto_check_hash_digest(kind: u32, digest: String) -> Promise { + wrap_api_future_void(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let digest: veilid_core::HashDigest = + veilid_core::deserialize_json(&digest).map_err(VeilidAPIError::generic)?; + + 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_check_hash_digest", + "kind", + kind.to_string(), + ) + })?; + csv.check_hash_digest(&digest)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn crypto_check_public_key(kind: u32, key: String) -> Promise { + wrap_api_future_void(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let key: veilid_core::PublicKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + + 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_check_public_key", + "kind", + kind.to_string(), + ) + })?; + csv.check_public_key(&key)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn crypto_check_secret_key(kind: u32, key: String) -> Promise { + wrap_api_future_void(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let key: veilid_core::SecretKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + + 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_check_secret_key", + "kind", + kind.to_string(), + ) + })?; + csv.check_secret_key(&key)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn crypto_check_signature(kind: u32, signature: String) -> Promise { + wrap_api_future_void(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let signature: veilid_core::Signature = + veilid_core::deserialize_json(&signature).map_err(VeilidAPIError::generic)?; + + 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_check_signature", + "kind", + kind.to_string(), + ) + })?; + csv.check_signature(&signature)?; + APIRESULT_UNDEFINED + }) +} + +#[wasm_bindgen()] +pub fn crypto_hash_password(kind: u32, password: String, salt: String) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let password: Vec = data_encoding::BASE64URL_NOPAD + .decode(password.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let salt: Vec = data_encoding::BASE64URL_NOPAD + .decode(salt.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_verify_password(kind: u32, password: String, password_hash: String) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let password: Vec = data_encoding::BASE64URL_NOPAD + .decode(password.as_bytes()) + .map_err(VeilidAPIError::generic)?; + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_derive_shared_secret(kind: u32, password: String, salt: String) -> Promise { + wrap_api_future_json(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + let password: Vec = data_encoding::BASE64URL_NOPAD + .decode(password.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let salt: Vec = data_encoding::BASE64URL_NOPAD + .decode(salt.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_random_nonce(kind: u32) -> Promise { + wrap_api_future_json(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_random_shared_secret(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_json(async move { + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_generate_key_pair(kind: u32) -> Promise { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + wrap_api_future_json(async move { + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_generate_hash(kind: u32, data: String) -> Promise { + wrap_api_future_json(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_validate_key_pair(kind: u32, key: String, secret: String) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let key: veilid_core::PublicKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let secret: veilid_core::SecretKey = + veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_validate_hash(kind: u32, data: String, hash: String) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let hash: veilid_core::HashDigest = + veilid_core::deserialize_json(&hash).map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_sign(kind: u32, key: String, secret: String, data: String) -> Promise { + wrap_api_future_json(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let key: veilid_core::PublicKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let secret: veilid_core::SecretKey = + veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; + + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_verify(kind: u32, key: String, data: String, signature: String) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let key: veilid_core::PublicKey = + veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; + let data: Vec = data_encoding::BASE64URL_NOPAD + .decode(data.as_bytes()) + .map_err(VeilidAPIError::generic)?; + let signature: veilid_core::Signature = + veilid_core::deserialize_json(&signature).map_err(VeilidAPIError::generic)?; + + 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()) + })?; + let out = csv.verify(&key, &data, &signature)?; + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +pub fn crypto_decrypt_aead( + kind: u32, + body: String, + nonce: String, + shared_secret: String, + associated_data: Option, +) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let body: Vec = data_encoding::BASE64URL_NOPAD + .decode(body.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let nonce: veilid_core::Nonce = + veilid_core::deserialize_json(&nonce).map_err(VeilidAPIError::generic)?; + + let shared_secret: veilid_core::SharedSecret = + veilid_core::deserialize_json(&shared_secret).map_err(VeilidAPIError::generic)?; + + let associated_data: Option> = match associated_data { + Some(ad) => Some( + data_encoding::BASE64URL_NOPAD + .decode(ad.as_bytes()) + .map_err(VeilidAPIError::generic)?, + ), + None => None, + }; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_encrypt_aead( + kind: u32, + body: String, + nonce: String, + shared_secret: String, + associated_data: Option, +) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let body: Vec = data_encoding::BASE64URL_NOPAD + .decode(body.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let nonce: veilid_core::Nonce = + veilid_core::deserialize_json(&nonce).map_err(VeilidAPIError::generic)?; + + let shared_secret: veilid_core::SharedSecret = + veilid_core::deserialize_json(&shared_secret).map_err(VeilidAPIError::generic)?; + + let associated_data: Option> = match associated_data { + Some(ad) => Some( + data_encoding::BASE64URL_NOPAD + .decode(ad.as_bytes()) + .map_err(VeilidAPIError::generic)?, + ), + None => None, + }; + + 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) + }) +} + +#[wasm_bindgen()] +pub fn crypto_crypt_no_auth( + kind: u32, + body: String, + nonce: String, + shared_secret: String, +) -> Promise { + wrap_api_future_plain(async move { + let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); + + let mut body: Vec = data_encoding::BASE64URL_NOPAD + .decode(body.as_bytes()) + .map_err(VeilidAPIError::generic)?; + + let nonce: veilid_core::Nonce = + veilid_core::deserialize_json(&nonce).map_err(VeilidAPIError::generic)?; + + let shared_secret: veilid_core::SharedSecret = + veilid_core::deserialize_json(&shared_secret).map_err(VeilidAPIError::generic)?; + + 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) + }) +} + +#[wasm_bindgen()] +#[must_use] +pub fn now() -> String { + veilid_core::Timestamp::now().as_u64().to_string() +} + +#[wasm_bindgen()] +pub fn debug(command: String) -> Promise { + wrap_api_future_plain(async move { + let veilid_api = get_veilid_api()?; + let out = veilid_api.debug(command).await?; + APIResult::Ok(out) + }) +} + +#[wasm_bindgen()] +#[must_use] +pub fn veilid_version_string() -> String { + veilid_core::veilid_version_string() +} + +#[wasm_bindgen()] +#[must_use] +pub fn veilid_version() -> JsValue { + let (major, minor, patch) = veilid_core::veilid_version(); + let vv = VeilidVersion { + major, + minor, + patch, + }; + ::from_serde(&vv).unwrap() +} + +#[wasm_bindgen()] +#[must_use] +pub fn default_veilid_config() -> String { + veilid_core::default_veilid_config() +} diff --git a/veilid-wasm/src/js/mod.rs b/veilid-wasm/src/js/mod.rs new file mode 100644 index 00000000..4d18c323 --- /dev/null +++ b/veilid-wasm/src/js/mod.rs @@ -0,0 +1,28 @@ +/// Veilid WASM Bindings for Native Javascript / Typescript +pub mod veilid_client_js; +pub mod veilid_crypto_js; +pub mod veilid_routing_context_js; +pub mod veilid_table_db_js; + +use super::*; + +// API Singleton +lazy_static! { + static ref VEILID_API: SendWrapper>> = + SendWrapper::new(RefCell::new(None)); + static ref FILTERS: SendWrapper>> = + SendWrapper::new(RefCell::new(BTreeMap::new())); +} + +fn get_veilid_api() -> Result { + (*VEILID_API) + .borrow() + .clone() + .ok_or(veilid_core::VeilidAPIError::NotInitialized) +} + +fn take_veilid_api() -> Result { + (**VEILID_API) + .take() + .ok_or(veilid_core::VeilidAPIError::NotInitialized) +} diff --git a/veilid-wasm/src/veilid_client_js.rs b/veilid-wasm/src/js/veilid_client_js.rs similarity index 74% rename from veilid-wasm/src/veilid_client_js.rs rename to veilid-wasm/src/js/veilid_client_js.rs index 7ae6e525..2e5a65ce 100644 --- a/veilid-wasm/src/veilid_client_js.rs +++ b/veilid-wasm/src/js/veilid_client_js.rs @@ -1,5 +1,7 @@ #![allow(non_snake_case)] use super::*; +use veilid_crypto_js::VeilidCrypto; +use wasm_bindgen_derive::try_from_js_array; #[wasm_bindgen(typescript_custom_section)] const IUPDATE_VEILID_FUNCTION: &'static str = r#" @@ -12,6 +14,8 @@ extern "C" { pub type UpdateVeilidFunction; } +static INITIALIZED: AtomicBool = AtomicBool::new(false); + #[wasm_bindgen(js_name = veilidClient)] pub struct VeilidClient {} @@ -34,7 +38,7 @@ impl VeilidClient { // Performance logger if platformConfig.logging.performance.enabled { - let filter = veilid_core::VeilidLayerFilter::new( + let filter = VeilidLayerFilter::new( platformConfig.logging.performance.level, &platformConfig.logging.performance.ignore_log_targets, None, @@ -47,7 +51,7 @@ impl VeilidClient { } else { ConsoleConfig::NoReporting }) - .with_field_filter(Some(Arc::new(|k| k != veilid_core::VEILID_LOG_KEY_FIELD))), + .with_field_filter(Some(Arc::new(|k| k != VEILID_LOG_KEY_FIELD))), ) .with_filter(filter.clone()); filters.insert("performance", filter); @@ -56,12 +60,12 @@ impl VeilidClient { // API logger if platformConfig.logging.api.enabled { - let filter = veilid_core::VeilidLayerFilter::new( + let filter = VeilidLayerFilter::new( platformConfig.logging.api.level, &platformConfig.logging.api.ignore_log_targets, None, ); - let layer = veilid_core::ApiTracingLayer::init().with_filter(filter.clone()); + let layer = ApiTracingLayer::init().with_filter(filter.clone()); filters.insert("api", filter); layers.push(layer.boxed()); } @@ -78,7 +82,7 @@ impl VeilidClient { /// Must be called only once at the start of an application /// /// @param {UpdateVeilidFunction} update_callback_js - called when internal state of the Veilid node changes, for example, when app-level messages are received, when private routes die and need to be reallocated, or when routing table states change - /// @param {string} json_config - called at startup to supply a JSON configuration object. + /// @param {VeilidConfig} config - the configuration object to use for the instance pub async fn startupCore( update_callback_js: UpdateVeilidFunction, config: VeilidConfig, @@ -99,10 +103,10 @@ impl VeilidClient { }); if VEILID_API.borrow().is_some() { - return APIResult::Err(veilid_core::VeilidAPIError::AlreadyInitialized); + return APIResult::Err(VeilidAPIError::AlreadyInitialized); } - let veilid_api = veilid_core::api_startup(update_callback, config).await?; + let veilid_api = api_startup(update_callback, config).await?; VEILID_API.replace(Some(veilid_api)); APIRESULT_UNDEFINED } @@ -154,7 +158,7 @@ impl VeilidClient { /// Check if Veilid is shutdown. pub fn isShutdown() -> APIResult { let veilid_api = get_veilid_api(); - if let Err(veilid_core::VeilidAPIError::NotInitialized) = veilid_api { + if let Err(VeilidAPIError::NotInitialized) = veilid_api { return APIResult::Ok(true); } let veilid_api = veilid_api.unwrap(); @@ -183,15 +187,49 @@ impl VeilidClient { APIRESULT_UNDEFINED } - /// Create a new MemberId for use with in creating `DHTSchema`s. - pub fn generateMemberId(writer_key: String) -> APIResult { - let writer_key: veilid_core::PublicKey = PublicKey::from_str(&writer_key)?; + /// Get a cryptosystem by its kind + pub fn getCrypto(kind: CryptoKind) -> APIResult { + let veilid_api = get_veilid_api()?; + if veilid_api.crypto()?.get(kind).is_none() { + apibail_invalid_argument!("get_crypto", "kind", kind); + } + APIResult::Ok(VeilidCrypto { kind }) + } + + /// Verify multiple signatures with multiple cryptosystems + pub fn verifySignatures( + #[wasm_bindgen(unchecked_param_type = "PublicKey[]")] public_keys: JsValue, + data: Box<[u8]>, + #[wasm_bindgen(unchecked_param_type = "Signature[]")] signatures: JsValue, + ) -> VeilidAPIResult>> { + let public_keys = + try_from_js_array::(public_keys).map_err(VeilidAPIError::generic)?; + let signatures = + try_from_js_array::(signatures).map_err(VeilidAPIError::generic)?; let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let out = crypto.verify_signatures(&public_keys, &data, &signatures)?; + APIResult::Ok(out.map(|v| v.iter().cloned().collect())) + } - let member_id = veilid_api.generate_member_id(&writer_key)?; + /// Generate multiple signatures with multiple cryptosystems + pub fn generateSignatures( + data: Box<[u8]>, + #[wasm_bindgen(unchecked_param_type = "KeyPair[]")] key_pairs: JsValue, + ) -> APIResult> { + let key_pairs = try_from_js_array::(key_pairs).map_err(VeilidAPIError::generic)?; + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let out = crypto.generate_signatures(&data, &key_pairs, |_k, s| s)?; + APIResult::Ok(out) + } - APIResult::Ok(member_id.to_string()) + /// Create a new MemberId for use with in creating `DHTSchema`s. + pub fn generateMemberId(writer_key: &PublicKey) -> APIResult { + let veilid_api = get_veilid_api()?; + let member_id = veilid_api.generate_member_id(writer_key)?; + APIResult::Ok(member_id) } /// Allocate a new private route set with default cryptography and network options. @@ -199,23 +237,22 @@ impl VeilidClient { /// Those nodes importing the blob will have their choice of which crypto kind to use. /// /// Returns a route id and 'blob' that can be published over some means (DHT or otherwise) to be imported by another Veilid node. - pub async fn newPrivateRoute() -> APIResult { + pub async fn newPrivateRoute() -> APIResult { let veilid_api = get_veilid_api()?; - let (route_id, blob) = veilid_api.new_private_route().await?; + let route_blob = veilid_api.new_private_route().await?; - let route_blob = VeilidRouteBlob { route_id, blob }; APIResult::Ok(route_blob) } /// Import a private route blob as a remote private route. /// /// Returns a route id that can be used to send private messages to the node creating this route. - pub fn importRemotePrivateRoute(&self, blob: String) -> APIResult { - let blob = unmarshall(blob)?; + #[allow(clippy::boxed_local)] + pub fn importRemotePrivateRoute(&self, blob: Box<[u8]>) -> APIResult { let veilid_api = get_veilid_api()?; - let route_id = veilid_api.import_remote_private_route(blob)?; - APIResult::Ok(route_id.to_string()) + let route_id = veilid_api.import_remote_private_route(blob.to_vec())?; + APIResult::Ok(route_id) } /// Allocate a new private route and specify a specific cryptosystem, stability and sequencing preference. @@ -226,24 +263,22 @@ impl VeilidClient { pub async fn newCustomPrivateRoute( stability: Stability, sequencing: Sequencing, - ) -> APIResult { + ) -> APIResult { let veilid_api = get_veilid_api()?; - let (route_id, blob) = veilid_api - .new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing) + let route_blob = veilid_api + .new_custom_private_route(&VALID_CRYPTO_KINDS, stability, sequencing) .await?; - let route_blob = VeilidRouteBlob { route_id, blob }; APIResult::Ok(route_blob) } /// Release either a locally allocated or remotely imported private route. /// /// This will deactivate the route and free its resources and it can no longer be sent to or received from. - pub fn releasePrivateRoute(route_id: String) -> APIResult<()> { - let route_id: veilid_core::RouteId = RouteId::from_str(&route_id)?; + pub fn releasePrivateRoute(route_id: &RouteId) -> APIResult<()> { let veilid_api = get_veilid_api()?; - veilid_api.release_private_route(route_id)?; + veilid_api.release_private_route(route_id.clone())?; APIRESULT_UNDEFINED } @@ -252,13 +287,11 @@ impl VeilidClient { /// * `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 pub async fn appCallReply(call_id: String, message: Box<[u8]>) -> APIResult<()> { - let message = message.into_vec(); + let message = message.to_vec(); let call_id = match call_id.parse() { Ok(v) => v, Err(e) => { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( - e, "call_id", call_id, - )) + return APIResult::Err(VeilidAPIError::invalid_argument(e, "call_id", call_id)) } }; let veilid_api = get_veilid_api()?; @@ -269,7 +302,7 @@ impl VeilidClient { /// Get the current timestamp, in string format #[must_use] pub fn now() -> String { - veilid_core::Timestamp::now().as_u64().to_string() + Timestamp::now().as_u64().to_string() } /// Execute an 'internal debug command'. @@ -282,7 +315,7 @@ impl VeilidClient { /// Return the cargo package version of veilid-core, in object format. #[must_use] pub fn version() -> VeilidVersion { - let (major, minor, patch) = veilid_core::veilid_version(); + let (major, minor, patch) = veilid_version(); super::VeilidVersion { major, minor, @@ -293,13 +326,13 @@ impl VeilidClient { /// Return the features that were enabled when veilid-core was built. #[must_use] pub fn features() -> Vec { - veilid_core::veilid_features() + veilid_features() } /// Return the cargo package version of veilid-core, in string format. #[must_use] pub fn versionString() -> String { - veilid_core::veilid_version_string() + veilid_version_string() } /// Return the default veilid configuration, in string format diff --git a/veilid-wasm/src/js/veilid_crypto_js.rs b/veilid-wasm/src/js/veilid_crypto_js.rs new file mode 100644 index 00000000..d9263bcd --- /dev/null +++ b/veilid-wasm/src/js/veilid_crypto_js.rs @@ -0,0 +1,331 @@ +#![allow(non_snake_case)] +use super::*; + +#[wasm_bindgen(js_name = veilidCrypto)] +pub struct VeilidCrypto { + pub(crate) kind: CryptoKind, +} + +#[wasm_bindgen(js_class = veilidCrypto)] +impl VeilidCrypto { + fn with_crypto_system< + T, + F: FnOnce(&(dyn CryptoSystem + Send + Sync + 'static)) -> APIResult, + >( + &self, + closure: F, + ) -> APIResult { + let veilid_api = get_veilid_api()?; + let crypto = veilid_api.crypto()?; + let crypto_system = crypto.get(self.kind).ok_or_else(|| { + VeilidAPIError::invalid_argument("with_crypto_system", "kind", self.kind.to_string()) + })?; + closure(crypto_system.deref()) + } + + pub fn cachedDh(&self, key: &PublicKey, secret: &SecretKey) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.cached_dh(key, secret)?; + APIResult::Ok(out) + }) + } + + pub fn computeDh(&self, key: &PublicKey, secret: &SecretKey) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.compute_dh(key, secret)?; + APIResult::Ok(out) + }) + } + + pub fn generateSharedSecret( + &self, + key: &PublicKey, + secret: &SecretKey, + domain: Box<[u8]>, + ) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.generate_shared_secret(key, secret, &domain)?; + APIResult::Ok(out) + }) + } + + pub fn randomBytes(&self, len: u32) -> APIResult> { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.random_bytes(len); + let out = out.into_boxed_slice(); + APIResult::Ok(out) + }) + } + + pub fn sharedSecretLength(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.shared_secret_length(); + APIResult::Ok(out) + }) + } + + pub fn nonceLength(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.nonce_length(); + APIResult::Ok(out) + }) + } + + pub fn hashDigestLength(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.hash_digest_length(); + APIResult::Ok(out) + }) + } + + pub fn publicKeyLength(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.public_key_length(); + APIResult::Ok(out) + }) + } + + pub fn secretKeyLength(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.secret_key_length(); + APIResult::Ok(out) + }) + } + + pub fn signatureLength(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.signature_length(); + APIResult::Ok(out) + }) + } + + pub fn defaultSaltLength(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.default_salt_length(); + APIResult::Ok(out) + }) + } + + pub fn aeadOverhead(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.aead_overhead(); + APIResult::Ok(out) + }) + } + + pub fn checkSharedSecret(&self, secret: &SharedSecret) -> APIResult<()> { + self.with_crypto_system(|crypto_system| { + crypto_system.check_shared_secret(secret)?; + APIRESULT_UNDEFINED + }) + } + + pub fn checkNonce(&self, nonce: &Nonce) -> APIResult<()> { + self.with_crypto_system(|crypto_system| { + crypto_system.check_nonce(nonce)?; + APIRESULT_UNDEFINED + }) + } + + pub fn checkHashDigest(&self, digest: &HashDigest) -> APIResult<()> { + self.with_crypto_system(|crypto_system| { + crypto_system.check_hash_digest(digest)?; + APIRESULT_UNDEFINED + }) + } + + pub fn checkPublicKey(&self, key: &PublicKey) -> APIResult<()> { + self.with_crypto_system(|crypto_system| { + crypto_system.check_public_key(key)?; + APIRESULT_UNDEFINED + }) + } + + pub fn checkSecretKey(&self, key: &SecretKey) -> APIResult<()> { + self.with_crypto_system(|crypto_system| { + crypto_system.check_secret_key(key)?; + APIRESULT_UNDEFINED + }) + } + + pub fn checkSignature(&self, signature: &Signature) -> APIResult<()> { + self.with_crypto_system(|crypto_system| { + crypto_system.check_signature(signature)?; + APIRESULT_UNDEFINED + }) + } + + pub fn hashPassword(&self, password: Box<[u8]>, salt: Box<[u8]>) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.hash_password(&password, &salt)?; + APIResult::Ok(out) + }) + } + + pub fn verifyPassword(&self, password: Box<[u8]>, password_hash: String) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.verify_password(&password, &password_hash)?; + APIResult::Ok(out) + }) + } + + pub fn deriveSharedSecret( + &self, + password: Box<[u8]>, + salt: Box<[u8]>, + ) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.derive_shared_secret(&password, &salt)?; + APIResult::Ok(out) + }) + } + + pub fn randomNonce(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.random_nonce(); + APIResult::Ok(out) + }) + } + + pub fn randomSharedSecret(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.random_shared_secret(); + APIResult::Ok(out) + }) + } + + pub fn generateKeyPair(&self) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.generate_keypair(); + APIResult::Ok(out) + }) + } + + pub fn generateHash(&self, data: Box<[u8]>) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.generate_hash(&data); + APIResult::Ok(out) + }) + } + + pub fn validateKeyPair(&self, key: &PublicKey, secret: &SecretKey) -> APIResult { + self.with_crypto_system(|crypto_system| crypto_system.validate_keypair(key, secret)) + } + + pub fn validateHash(&self, data: Box<[u8]>, hash: &HashDigest) -> APIResult { + self.with_crypto_system(|crypto_system| crypto_system.validate_hash(&data, hash)) + } + + pub fn sign( + &self, + key: &PublicKey, + secret: &SecretKey, + data: Box<[u8]>, + ) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.sign(key, secret, &data)?; + APIResult::Ok(out) + }) + } + + pub fn verify( + &self, + key: &PublicKey, + data: Box<[u8]>, + signature: &Signature, + ) -> APIResult { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.verify(key, &data, signature)?; + APIResult::Ok(out) + }) + } + + pub fn decryptAead( + &self, + body: Box<[u8]>, + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option>, + ) -> APIResult> { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.decrypt_aead( + &body, + nonce, + shared_secret, + match &associated_data { + Some(ad) => Some(ad), + None => None, + }, + )?; + let out = out.into_boxed_slice(); + APIResult::Ok(out) + }) + } + + pub fn encryptAead( + &self, + body: Box<[u8]>, + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option>, + ) -> APIResult> { + self.with_crypto_system(|crypto_system| { + let out = crypto_system.encrypt_aead( + &body, + nonce, + shared_secret, + match &associated_data { + Some(ad) => Some(ad), + None => None, + }, + )?; + APIResult::Ok(out.into_boxed_slice()) + }) + } + + pub fn cryptNoAuth( + &self, + mut body: Box<[u8]>, + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> APIResult> { + self.with_crypto_system(|crypto_system| { + crypto_system.crypt_in_place_no_auth(&mut body, nonce, shared_secret)?; + APIResult::Ok(body) + }) + } + + // -------------------------------- + // Constants + // (written as getters since wasm_bindgen doesn't support export of const) + // -------------------------------- + + /// The VLD0 crypto kind + #[cfg(feature = "enable-crypto-vld0")] + #[wasm_bindgen(getter)] + pub fn CRYPTO_KIND_VLD0() -> CryptoKind { + CRYPTO_KIND_VLD0 + } + + /// The NONE crypto kind + #[cfg(feature = "enable-crypto-none")] + #[wasm_bindgen(getter)] + pub fn CRYPTO_KIND_NONE() -> CryptoKind { + CRYPTO_KIND_NONE + } + + // /// The VLD1 crypto kind + // #[cfg(feature = "enable-crypto-vld1")] + // #[wasm_bindgen(getter)] + // #[must_use] + // pub fn CRYPTO_KIND_VLD1() -> CryptoKind { + // CRYPTO_KIND_VLD1 + // } + + /// All crypto kinds supported by this configuration of Veilid + #[wasm_bindgen(getter)] + #[must_use] + pub fn VALID_CRYPTO_KINDS() -> Vec { + VALID_CRYPTO_KINDS.to_vec() + } +} diff --git a/veilid-wasm/src/veilid_routing_context_js.rs b/veilid-wasm/src/js/veilid_routing_context_js.rs similarity index 86% rename from veilid-wasm/src/veilid_routing_context_js.rs rename to veilid-wasm/src/js/veilid_routing_context_js.rs index e36b2f9f..5020051f 100644 --- a/veilid-wasm/src/veilid_routing_context_js.rs +++ b/veilid-wasm/src/js/veilid_routing_context_js.rs @@ -1,5 +1,6 @@ #![allow(non_snake_case)] use super::*; +use wasm_bindgen_derive::try_from_js_option; #[wasm_bindgen()] pub struct VeilidRoutingContext { @@ -111,39 +112,41 @@ impl VeilidRoutingContext { /// Deterministicly builds the record key for a given schema and owner public key. /// The crypto kind of the record key will be that of the `owner` public key #[allow(clippy::unused_async)] - pub async fn getDhtRecordKey(&self, schema: DHTSchema, owner: String) -> APIResult { - let owner = PublicKey::from_str(&owner)?; + pub async fn getDhtRecordKey( + &self, + schema: DHTSchema, + owner: &PublicKey, + ) -> APIResult { let routing_context = self.getRoutingContext()?; - let key = routing_context.get_dht_record_key(schema, &owner)?; - APIResult::Ok(key.to_string()) + let key = routing_context.get_dht_record_key(schema, owner)?; + APIResult::Ok(key) } /// Creates a new DHT record /// /// The record is considered 'open' after the create operation succeeds. + /// * 'kind' - specify a cryptosystem kind to use /// * 'schema' - the schema to use when creating the DHT record /// * 'owner' - optionally specify an owner keypair to use. If you leave this as None then a random one will be generated. If specified, the crypto kind of the owner must match that of the `kind` parameter - /// * 'kind' - specify a cryptosystem kind to use. Normally you will leave this as None to choose the 'best' cryptosystem available. /// Returns the newly allocated DHT record's key if successful. /// /// Note: if you pass in an owner keypair this call is a deterministic! This means that if you try to create a new record for a given owner and schema that already exists it *will* fail. pub async fn createDhtRecord( &self, + kind: CryptoKind, schema: DHTSchema, - owner: Option, - kind: Option, + owner: Option, ) -> APIResult { - let crypto_kind = kind - .map(|kind| veilid_core::CryptoKind::from_str(&kind)) - .map_or(APIResult::Ok(None), |r| r.map(Some))?; - let owner_keypair = owner - .map(|owner| KeyPair::from_str(&owner)) - .map_or(APIResult::Ok(None), |r| r.map(Some))?; + let owner = match owner { + Some(owner) => try_from_js_option::(owner).map_err(VeilidAPIError::generic)?, + None => None, + }; + let routing_context = self.getRoutingContext()?; let dht_record_descriptor = routing_context - .create_dht_record(schema, owner_keypair, crypto_kind) + .create_dht_record(kind, schema, owner) .await?; APIResult::Ok(dht_record_descriptor) } @@ -164,26 +167,22 @@ impl VeilidRoutingContext { #[wasm_bindgen(skip_jsdoc)] pub async fn openDhtRecord( &self, - key: String, - default_writer: Option, + key: &RecordKey, + default_writer: Option, ) -> APIResult { - let key = RecordKey::from_str(&key)?; - let default_writer = default_writer - .map(|default_writer| KeyPair::from_str(&default_writer)) - .map_or(APIResult::Ok(None), |r| r.map(Some))?; - let routing_context = self.getRoutingContext()?; - let dht_record_descriptor = routing_context.open_dht_record(key, default_writer).await?; + let dht_record_descriptor = routing_context + .open_dht_record(key.clone(), default_writer) + .await?; APIResult::Ok(dht_record_descriptor) } /// Closes a DHT record at a specific key that was opened with create_dht_record or open_dht_record. /// /// Closing a record allows you to re-open it with a different routing context - pub async fn closeDhtRecord(&self, key: String) -> APIResult<()> { - let key = RecordKey::from_str(&key)?; + pub async fn closeDhtRecord(&self, key: &RecordKey) -> APIResult<()> { let routing_context = self.getRoutingContext()?; - routing_context.close_dht_record(key).await?; + routing_context.close_dht_record(key.clone()).await?; APIRESULT_UNDEFINED } @@ -192,10 +191,9 @@ impl VeilidRoutingContext { /// If the record is opened, it must be closed before it is deleted. /// Deleting a record does not delete it from the network, but will remove the storage of the record /// locally, and will prevent its value from being refreshed on the network by this node. - pub async fn deleteDhtRecord(&self, key: String) -> APIResult<()> { - let key = RecordKey::from_str(&key)?; + pub async fn deleteDhtRecord(&self, key: &RecordKey) -> APIResult<()> { let routing_context = self.getRoutingContext()?; - routing_context.delete_dht_record(key).await?; + routing_context.delete_dht_record(key.clone()).await?; APIRESULT_UNDEFINED } @@ -207,14 +205,13 @@ impl VeilidRoutingContext { /// Returns a Uint8Array of `data` if the value subkey has valid data. pub async fn getDhtValue( &self, - key: String, + key: &RecordKey, subkey: u32, forceRefresh: bool, ) -> APIResult> { - let key = RecordKey::from_str(&key)?; let routing_context = self.getRoutingContext()?; let res = routing_context - .get_dht_value(key, subkey, forceRefresh) + .get_dht_value(key.clone(), subkey, forceRefresh) .await?; APIResult::Ok(res) } @@ -228,17 +225,24 @@ impl VeilidRoutingContext { /// Returns a Uint8Array of `data` if the value put was older than the one available on the network. pub async fn setDhtValue( &self, - key: String, + key: &RecordKey, subkey: u32, data: Box<[u8]>, - options: Option, + options: Option, ) -> APIResult> { - let key = RecordKey::from_str(&key)?; let data = data.into_vec(); let routing_context = self.getRoutingContext()?; let res = routing_context - .set_dht_value(key, subkey, data, options) + .set_dht_value( + key.clone(), + subkey, + data, + match options { + Some(o) => Some(o.try_into()?), + None => None, + }, + ) .await?; APIResult::Ok(res) } @@ -270,22 +274,14 @@ impl VeilidRoutingContext { /// Members can be specified via the SMPL schema and do not need to allocate writable subkeys in order to offer a member watch capability. pub async fn watchDhtValues( &self, - key: String, + key: &RecordKey, subkeys: Option, - expiration: Option, + expiration: Option, count: Option, ) -> APIResult { - let key = RecordKey::from_str(&key)?; - let expiration = if let Some(expiration) = expiration { - Some(veilid_core::Timestamp::new( - u64::from_str(&expiration).map_err(VeilidAPIError::generic)?, - )) - } else { - None - }; let routing_context = self.getRoutingContext()?; let res = routing_context - .watch_dht_values(key, subkeys, expiration, count) + .watch_dht_values(key.clone(), subkeys, expiration, count) .await?; APIResult::Ok(res) } @@ -302,12 +298,13 @@ impl VeilidRoutingContext { /// Returns Ok(false) if the entire watch has been cancelled. pub async fn cancelDhtWatch( &self, - key: String, + key: &RecordKey, subkeys: Option, ) -> APIResult { - let key = RecordKey::from_str(&key)?; let routing_context = self.getRoutingContext()?; - let res = routing_context.cancel_dht_watch(key, subkeys).await?; + let res = routing_context + .cancel_dht_watch(key.clone(), subkeys) + .await?; APIResult::Ok(res) } @@ -352,16 +349,15 @@ impl VeilidRoutingContext { /// Returns a DHTRecordReport with the subkey ranges that were returned that overlapped the schema, and sequence numbers for each of the subkeys in the range. pub async fn inspectDhtRecord( &self, - key: String, + key: &RecordKey, subkeys: Option, scope: Option, ) -> APIResult { - let key = RecordKey::from_str(&key)?; let scope = scope.unwrap_or_default(); let routing_context = self.getRoutingContext()?; let res = routing_context - .inspect_dht_record(key, subkeys, scope) + .inspect_dht_record(key.clone(), subkeys, scope) .await?; APIResult::Ok(res) } diff --git a/veilid-wasm/src/veilid_table_db_js.rs b/veilid-wasm/src/js/veilid_table_db_js.rs similarity index 100% rename from veilid-wasm/src/veilid_table_db_js.rs rename to veilid-wasm/src/js/veilid_table_db_js.rs diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index 967ed059..88d458d3 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -2,9 +2,7 @@ #![no_std] #![recursion_limit = "256"] -/// Veilid WASM Bindings for Flutter/Dart, as well as Native Javascript -/// The Flutter/Dart bindings are in this lib.rs directly -/// The Native Javascript bindings are in the other files. +/// Veilid WASM Bindings extern crate alloc; use alloc::string::String; use alloc::sync::Arc; @@ -12,8 +10,6 @@ use alloc::*; use core::cell::RefCell; use core::fmt::Debug; use core::sync::atomic::{AtomicBool, Ordering}; -use futures_util::FutureExt; -use gloo_utils::format::JsValueSerdeExt; use js_sys::*; use lazy_static::*; use send_wrapper::*; @@ -26,1894 +22,29 @@ use veilid_core::{tools::*, VeilidAPIError}; use veilid_tracing_wasm::*; 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; +cfg_if::cfg_if! { + if #[cfg(feature="dart")] { + use futures_util::FutureExt; + use gloo_utils::format::JsValueSerdeExt; + + pub mod dart; + } +} + +cfg_if::cfg_if! { + if #[cfg(feature="js")] { + pub mod js; + } +} + +pub mod api_result; +pub mod veilid_version; +pub mod veilid_wasm_config; mod wasm_helpers; + +pub use api_result::*; +pub use veilid_version::*; +pub use veilid_wasm_config::*; + use wasm_helpers::*; - -// API Singleton -lazy_static! { - static ref VEILID_API: SendWrapper>> = - SendWrapper::new(RefCell::new(None)); - static ref FILTERS: SendWrapper>> = - SendWrapper::new(RefCell::new(BTreeMap::new())); - static ref ROUTING_CONTEXTS: SendWrapper>> = - SendWrapper::new(RefCell::new(BTreeMap::new())); - static ref TABLE_DBS: SendWrapper>> = - SendWrapper::new(RefCell::new(BTreeMap::new())); - static ref TABLE_DB_TRANSACTIONS: SendWrapper>> = - SendWrapper::new(RefCell::new(BTreeMap::new())); -} - -fn get_veilid_api() -> Result { - (*VEILID_API) - .borrow() - .clone() - .ok_or(veilid_core::VeilidAPIError::NotInitialized) -} - -fn take_veilid_api() -> Result { - (**VEILID_API) - .take() - .ok_or(veilid_core::VeilidAPIError::NotInitialized) -} - -// Marshalling helpers -pub fn unmarshall(b64: String) -> APIResult> { - data_encoding::BASE64URL_NOPAD - .decode(b64.as_bytes()) - .map_err(|e| { - VeilidAPIError::generic(format!( - "error decoding base64url string '{}' into bytes: {}", - b64, e - )) - }) -} - -#[must_use] -pub fn marshall(data: &[u8]) -> String { - data_encoding::BASE64URL_NOPAD.encode(data) -} - -// JSON Helpers for WASM -pub fn to_json(val: T) -> JsValue { - JsValue::from_str(&serialize_json(val)) -} - -pub fn to_jsvalue(val: T) -> JsValue -where - JsValue: From, -{ - JsValue::from(val) -} - -pub fn from_json( - val: JsValue, -) -> Result { - let s = val - .as_string() - .ok_or_else(|| veilid_core::VeilidAPIError::ParseError { - message: "Value is not String".to_owned(), - value: String::new(), - })?; - deserialize_json(&s) -} - -// Utility types for async API results -type APIResult = Result; -const APIRESULT_UNDEFINED: APIResult<()> = APIResult::Ok(()); - -pub fn wrap_api_future_json(future: F) -> Promise -where - F: Future> + 'static, - T: Serialize + Debug + 'static, -{ - future_to_promise(future.map(|res| res.map(|v| to_json(v)).map_err(to_json))) -} - -pub fn wrap_api_future_plain(future: F) -> Promise -where - F: Future> + 'static, - JsValue: From, - T: 'static, -{ - future_to_promise(future.map(|res| res.map(|v| to_jsvalue(v)).map_err(to_json))) -} - -pub fn wrap_api_future_void(future: F) -> Promise -where - F: Future> + 'static, -{ - future_to_promise(future.map(|res| res.map(|_| JsValue::UNDEFINED).map_err(to_json))) -} - -///////////////////////////////////////// -// WASM-specific - -#[derive(Debug, Deserialize, Serialize)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] -pub struct VeilidWASMConfigLoggingPerformance { - pub enabled: bool, - pub level: veilid_core::VeilidConfigLogLevel, - pub logs_in_timings: bool, - pub logs_in_console: bool, - pub ignore_log_targets: Vec, -} - -#[derive(Debug, Deserialize, Serialize)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] -pub struct VeilidWASMConfigLoggingAPI { - pub enabled: bool, - pub level: veilid_core::VeilidConfigLogLevel, - pub ignore_log_targets: Vec, -} - -#[derive(Debug, Deserialize, Serialize)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] -pub struct VeilidWASMConfigLogging { - pub performance: VeilidWASMConfigLoggingPerformance, - pub api: VeilidWASMConfigLoggingAPI, -} - -#[derive(Debug, Deserialize, Serialize)] -#[cfg_attr( - all(target_arch = "wasm32", target_os = "unknown"), - derive(Tsify), - tsify(from_wasm_abi) -)] -pub struct VeilidWASMConfig { - pub logging: VeilidWASMConfigLogging, -} - -#[derive(Debug, Deserialize, Serialize)] -#[cfg_attr( - all(target_arch = "wasm32", target_os = "unknown"), - 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( - all(target_arch = "wasm32", target_os = "unknown"), - tsify(type = "string") - )] - pub blob: Vec, -} - -// WASM Bindings - -#[wasm_bindgen()] -pub fn initialize_veilid_wasm() { - console_error_panic_hook::set_once(); -} - -static INITIALIZED: AtomicBool = AtomicBool::new(false); -#[wasm_bindgen()] -pub fn initialize_veilid_core(platform_config: String) { - if INITIALIZED.swap(true, Ordering::AcqRel) { - return; - } - let platform_config: VeilidWASMConfig = veilid_core::deserialize_json(&platform_config) - .expect("failed to deserialize platform config json"); - - // Set up subscriber and layers - let subscriber = Registry::default(); - let mut layers = Vec::new(); - let mut filters = (*FILTERS).borrow_mut(); - - // Performance logger - if platform_config.logging.performance.enabled { - let filter = veilid_core::VeilidLayerFilter::new( - platform_config.logging.performance.level, - &platform_config.logging.performance.ignore_log_targets, - None, - ); - let layer = WASMLayer::new( - WASMLayerConfig::new() - .with_report_logs_in_timings(platform_config.logging.performance.logs_in_timings) - .with_console_config(if platform_config.logging.performance.logs_in_console { - ConsoleConfig::ReportWithConsoleColor - } else { - ConsoleConfig::NoReporting - }) - .with_field_filter(Some(Arc::new(|k| k != veilid_core::VEILID_LOG_KEY_FIELD))), - ) - .with_filter(filter.clone()); - filters.insert("performance", filter); - layers.push(layer.boxed()); - }; - - // API logger - if platform_config.logging.api.enabled { - let filter = veilid_core::VeilidLayerFilter::new( - platform_config.logging.api.level, - &platform_config.logging.api.ignore_log_targets, - None, - ); - let layer = veilid_core::ApiTracingLayer::init().with_filter(filter.clone()); - filters.insert("api", filter); - layers.push(layer.boxed()); - } - - let subscriber = subscriber.with(layers); - subscriber - .try_init() - .map_err(|e| format!("failed to initialize logging: {}", e)) - .expect("failed to initalize WASM platform"); -} - -#[wasm_bindgen()] -pub fn change_log_level(layer: String, log_level: String) { - let layer = if layer == "all" { "".to_owned() } else { layer }; - let Ok(log_level) = deserialize_json::(&log_level) else { - return; - }; - let filters = (*FILTERS).borrow(); - if layer.is_empty() { - // Change all layers - for f in filters.values() { - f.set_max_level(log_level); - } - } else { - // Change a specific layer - if let Some(f) = filters.get(layer.as_str()) { - f.set_max_level(log_level); - } - } -} - -#[wasm_bindgen()] -pub fn change_log_ignore(layer: String, log_ignore: String) { - let layer = if layer == "all" { "".to_owned() } else { layer }; - - let filters = (*FILTERS).borrow(); - if layer.is_empty() { - // Change all layers - for f in filters.values() { - f.set_ignore_list(Some(VeilidLayerFilter::apply_ignore_change( - &f.ignore_list(), - log_ignore.clone(), - ))); - } - } else { - // Change a specific layer - if let Some(f) = filters.get(layer.as_str()) { - f.set_ignore_list(Some(VeilidLayerFilter::apply_ignore_change( - &f.ignore_list(), - log_ignore.clone(), - ))); - } - } -} - -#[wasm_bindgen()] -pub fn startup_veilid_core(update_callback_js: Function, json_config: String) -> Promise { - let update_callback_js = SendWrapper::new(update_callback_js); - wrap_api_future_void(async move { - let update_callback = Arc::new(move |update: VeilidUpdate| { - let _ret = - match Function::call1(&update_callback_js, &JsValue::UNDEFINED, &to_json(update)) { - Ok(v) => v, - Err(e) => { - console_log(&format!("calling update callback failed: {:?}", e)); - return; - } - }; - }); - - if VEILID_API.borrow().is_some() { - return Err(veilid_core::VeilidAPIError::AlreadyInitialized); - } - - let veilid_api = veilid_core::api_startup_json(update_callback, json_config).await?; - VEILID_API.replace(Some(veilid_api)); - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn get_veilid_state() -> Promise { - wrap_api_future_json(async move { - let veilid_api = get_veilid_api()?; - let core_state = veilid_api.get_state().await?; - APIResult::Ok(core_state) - }) -} - -#[wasm_bindgen()] -pub fn is_shutdown() -> APIResult { - let veilid_api = get_veilid_api(); - if let Err(veilid_core::VeilidAPIError::NotInitialized) = veilid_api { - return APIResult::Ok(true); - } - let veilid_api = veilid_api.unwrap(); - let is_shutdown = veilid_api.is_shutdown(); - APIResult::Ok(is_shutdown) -} - -#[wasm_bindgen()] -pub fn attach() -> Promise { - wrap_api_future_void(async move { - let veilid_api = get_veilid_api()?; - veilid_api.attach().await?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn detach() -> Promise { - wrap_api_future_void(async move { - let veilid_api = get_veilid_api()?; - veilid_api.detach().await?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn shutdown_veilid_core() -> Promise { - wrap_api_future_void(async move { - let veilid_api = take_veilid_api()?; - veilid_api.shutdown().await; - APIRESULT_UNDEFINED - }) -} - -fn add_routing_context(routing_context: veilid_core::RoutingContext) -> u32 { - let mut next_id: u32 = 1; - let mut rc = (*ROUTING_CONTEXTS).borrow_mut(); - while rc.contains_key(&next_id) { - next_id += 1; - } - rc.insert(next_id, routing_context); - next_id -} - -#[wasm_bindgen()] -pub fn routing_context() -> Promise { - wrap_api_future_plain(async move { - let veilid_api = get_veilid_api()?; - let routing_context = veilid_api.routing_context()?; - let new_id = add_routing_context(routing_context); - APIResult::Ok(new_id) - }) -} - -#[wasm_bindgen()] -#[must_use] -pub fn release_routing_context(id: u32) -> i32 { - let mut rc = (*ROUTING_CONTEXTS).borrow_mut(); - if rc.remove(&id).is_none() { - return 0; - } - 1 -} - -#[wasm_bindgen()] -#[must_use] -pub fn routing_context_with_default_safety(id: u32) -> u32 { - let routing_context = { - let rc = (*ROUTING_CONTEXTS).borrow(); - let Some(routing_context) = rc.get(&id) else { - return 0; - }; - routing_context.clone() - }; - let Ok(routing_context) = routing_context.with_default_safety() else { - return 0; - }; - add_routing_context(routing_context) -} - -#[wasm_bindgen()] -#[must_use] -pub fn routing_context_with_safety(id: u32, safety_selection: String) -> u32 { - let safety_selection: veilid_core::SafetySelection = - veilid_core::deserialize_json(&safety_selection).unwrap(); - - let routing_context = { - let rc = (*ROUTING_CONTEXTS).borrow(); - let Some(routing_context) = rc.get(&id) else { - return 0; - }; - routing_context.clone() - }; - let Ok(routing_context) = routing_context.with_safety(safety_selection) else { - return 0; - }; - add_routing_context(routing_context) -} - -#[wasm_bindgen()] -#[must_use] -pub fn routing_context_with_sequencing(id: u32, sequencing: String) -> u32 { - let sequencing: veilid_core::Sequencing = veilid_core::deserialize_json(&sequencing).unwrap(); - - let routing_context = { - let rc = (*ROUTING_CONTEXTS).borrow(); - let Some(routing_context) = rc.get(&id) else { - return 0; - }; - routing_context.clone() - }; - let routing_context = routing_context.with_sequencing(sequencing); - add_routing_context(routing_context) -} - -fn get_routing_context(id: u32, func_name: &str) -> APIResult { - let rc = (*ROUTING_CONTEXTS).borrow(); - let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( - func_name, "id", id, - )); - }; - Ok(routing_context.clone()) -} - -#[wasm_bindgen()] -pub fn routing_context_safety(id: u32) -> Promise { - wrap_api_future_json(async move { - let routing_context = get_routing_context(id, "routing_context_safety")?; - - let safety_selection = routing_context.safety(); - APIResult::Ok(safety_selection) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_app_call(id: u32, target: String, request: String) -> Promise { - wrap_api_future_plain(async move { - let target: veilid_core::Target = - veilid_core::deserialize_json(&target).map_err(VeilidAPIError::generic)?; - - let request: Vec = data_encoding::BASE64URL_NOPAD - .decode(request.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let routing_context = get_routing_context(id, "routing_context_app_call")?; - - let answer = routing_context.app_call(target, request).await?; - let answer = data_encoding::BASE64URL_NOPAD.encode(&answer); - APIResult::Ok(answer) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_app_message(id: u32, target: String, message: String) -> Promise { - wrap_api_future_void(async move { - let target: veilid_core::Target = - veilid_core::deserialize_json(&target).map_err(VeilidAPIError::generic)?; - - let message: Vec = data_encoding::BASE64URL_NOPAD - .decode(message.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let routing_context = get_routing_context(id, "routing_context_app_message")?; - - routing_context.app_message(target, message).await?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn routing_context_get_dht_record_key(id: u32, schema: String, owner: String) -> Promise { - wrap_api_future_json(async move { - let schema: veilid_core::DHTSchema = - veilid_core::deserialize_json(&schema).map_err(VeilidAPIError::generic)?; - let owner: veilid_core::PublicKey = - veilid_core::deserialize_json(&owner).map_err(VeilidAPIError::generic)?; - - let routing_context = get_routing_context(id, "routing_context_get_dht_record_key")?; - - let key = routing_context.get_dht_record_key(schema, &owner)?; - - APIResult::Ok(key) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_create_dht_record( - id: u32, - schema: String, - owner: Option, - kind: u32, -) -> Promise { - wrap_api_future_json(async move { - let crypto_kind = if kind == 0 { - None - } else { - Some(veilid_core::CryptoKind::from(kind)) - }; - let owner: Option = match owner { - Some(s) => Some(veilid_core::deserialize_json(&s).map_err(VeilidAPIError::generic)?), - None => None, - }; - let schema: veilid_core::DHTSchema = - veilid_core::deserialize_json(&schema).map_err(VeilidAPIError::generic)?; - - let routing_context = get_routing_context(id, "routing_context_create_dht_record")?; - - let dht_record_descriptor = routing_context - .create_dht_record(schema, owner, crypto_kind) - .await?; - APIResult::Ok(dht_record_descriptor) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_open_dht_record(id: u32, key: String, writer: Option) -> Promise { - wrap_api_future_json(async move { - let key: veilid_core::RecordKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let writer: Option = match writer { - Some(s) => Some(veilid_core::deserialize_json(&s).map_err(VeilidAPIError::generic)?), - None => None, - }; - - let routing_context = get_routing_context(id, "routing_context_open_dht_record")?; - - let dht_record_descriptor = routing_context.open_dht_record(key, writer).await?; - APIResult::Ok(dht_record_descriptor) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_close_dht_record(id: u32, key: String) -> Promise { - wrap_api_future_void(async move { - let key: veilid_core::RecordKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - - let routing_context = get_routing_context(id, "routing_context_close_dht_record")?; - - routing_context.close_dht_record(key).await?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn routing_context_delete_dht_record(id: u32, key: String) -> Promise { - wrap_api_future_void(async move { - let key: veilid_core::RecordKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - - let routing_context = get_routing_context(id, "routing_context_delete_dht_record")?; - - routing_context.delete_dht_record(key).await?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn routing_context_get_dht_value( - id: u32, - key: String, - subkey: u32, - force_refresh: bool, -) -> Promise { - wrap_api_future_json(async move { - let key: veilid_core::RecordKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - - let routing_context = get_routing_context(id, "routing_context_get_dht_value")?; - - let res = routing_context - .get_dht_value(key, subkey, force_refresh) - .await?; - APIResult::Ok(res) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_set_dht_value( - id: u32, - key: String, - subkey: u32, - data: String, - options: Option, -) -> Promise { - wrap_api_future_json(async move { - let key: veilid_core::RecordKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let data: Vec = data_encoding::BASE64URL_NOPAD - .decode(data.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let options: Option = match options { - Some(s) => Some(veilid_core::deserialize_json(&s).map_err(VeilidAPIError::generic)?), - None => None, - }; - - let routing_context = get_routing_context(id, "routing_context_set_dht_value")?; - - let res = routing_context - .set_dht_value(key, subkey, data, options) - .await?; - APIResult::Ok(res) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_watch_dht_values( - id: u32, - key: String, - subkeys: String, - expiration: String, - count: u32, -) -> Promise { - wrap_api_future_plain(async move { - let key: veilid_core::RecordKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let subkeys: veilid_core::ValueSubkeyRangeSet = - veilid_core::deserialize_json(&subkeys).map_err(VeilidAPIError::generic)?; - let expiration = veilid_core::Timestamp::new( - u64::from_str(&expiration).map_err(VeilidAPIError::generic)?, - ); - - let routing_context = get_routing_context(id, "routing_context_watch_dht_values")?; - - let res = routing_context - .watch_dht_values(key, Some(subkeys), Some(expiration), Some(count)) - .await?; - APIResult::Ok(res) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_cancel_dht_watch(id: u32, key: String, subkeys: String) -> Promise { - wrap_api_future_plain(async move { - let key: veilid_core::RecordKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let subkeys: veilid_core::ValueSubkeyRangeSet = - veilid_core::deserialize_json(&subkeys).map_err(VeilidAPIError::generic)?; - - let routing_context = get_routing_context(id, "routing_context_cancel_dht_watch")?; - - let res = routing_context.cancel_dht_watch(key, Some(subkeys)).await?; - APIResult::Ok(res) - }) -} - -#[wasm_bindgen()] -pub fn routing_context_inspect_dht_record( - id: u32, - key: String, - subkeys: String, - scope: String, -) -> Promise { - wrap_api_future_json(async move { - let key: veilid_core::RecordKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let subkeys: veilid_core::ValueSubkeyRangeSet = - veilid_core::deserialize_json(&subkeys).map_err(VeilidAPIError::generic)?; - let scope: veilid_core::DHTReportScope = - veilid_core::deserialize_json(&scope).map_err(VeilidAPIError::generic)?; - - let routing_context = get_routing_context(id, "routing_context_inspect_dht_record")?; - - let res = routing_context - .inspect_dht_record(key, Some(subkeys), scope) - .await?; - - APIResult::Ok(res) - }) -} - -#[wasm_bindgen()] -pub fn generate_member_id(writer_key: String) -> Promise { - wrap_api_future_json(async move { - let writer_key: veilid_core::PublicKey = - veilid_core::deserialize_json(&writer_key).map_err(VeilidAPIError::generic)?; - - let veilid_api = get_veilid_api()?; - - let member_id = veilid_api.generate_member_id(&writer_key)?; - - APIResult::Ok(member_id) - }) -} - -#[wasm_bindgen()] -pub fn new_private_route() -> Promise { - wrap_api_future_json(async move { - 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) - }) -} - -#[wasm_bindgen()] -pub fn new_custom_private_route(stability: String, sequencing: String) -> Promise { - wrap_api_future_json(async move { - let stability: veilid_core::Stability = - veilid_core::deserialize_json(&stability).map_err(VeilidAPIError::generic)?; - let sequencing: veilid_core::Sequencing = - veilid_core::deserialize_json(&sequencing).map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn import_remote_private_route(blob: String) -> Promise { - wrap_api_future_json(async move { - let blob: Vec = data_encoding::BASE64URL_NOPAD - .decode(blob.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let veilid_api = get_veilid_api()?; - - let key = veilid_api.import_remote_private_route(blob)?; - - APIResult::Ok(key) - }) -} - -#[wasm_bindgen()] -pub fn release_private_route(route_id: String) -> Promise { - wrap_api_future_void(async move { - let route_id: veilid_core::RouteId = - veilid_core::deserialize_json(&route_id).map_err(VeilidAPIError::generic)?; - let veilid_api = get_veilid_api()?; - veilid_api.release_private_route(route_id)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn app_call_reply(call_id: String, message: String) -> Promise { - wrap_api_future_void(async move { - let message: Vec = data_encoding::BASE64URL_NOPAD - .decode(message.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let call_id = match call_id.parse() { - Ok(v) => v, - Err(e) => { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( - e, "call_id", call_id, - )) - } - }; - let veilid_api = get_veilid_api()?; - veilid_api.app_call_reply(call_id, message).await?; - APIRESULT_UNDEFINED - }) -} - -fn add_table_db(table_db: veilid_core::TableDB) -> u32 { - let mut next_id: u32 = 1; - let mut tdbs = (*TABLE_DBS).borrow_mut(); - while tdbs.contains_key(&next_id) { - next_id += 1; - } - tdbs.insert(next_id, table_db); - next_id -} - -#[wasm_bindgen()] -pub fn open_table_db(name: String, column_count: u32) -> Promise { - wrap_api_future_plain(async move { - let veilid_api = get_veilid_api()?; - let tstore = veilid_api.table_store()?; - let table_db = tstore - .open(&name, column_count) - .await - .map_err(veilid_core::VeilidAPIError::generic)?; - let new_id = add_table_db(table_db); - APIResult::Ok(new_id) - }) -} - -#[wasm_bindgen()] -#[must_use] -pub fn release_table_db(id: u32) -> i32 { - let mut tdbs = (*TABLE_DBS).borrow_mut(); - if tdbs.remove(&id).is_none() { - return 0; - } - 1 -} - -#[wasm_bindgen()] -pub fn delete_table_db(name: String) -> Promise { - wrap_api_future_plain(async move { - let veilid_api = get_veilid_api()?; - let tstore = veilid_api.table_store()?; - let deleted = tstore - .delete(&name) - .await - .map_err(veilid_core::VeilidAPIError::generic)?; - APIResult::Ok(deleted) - }) -} - -#[wasm_bindgen()] -#[must_use] -pub fn table_db_get_column_count(id: u32) -> u32 { - let table_dbs = (*TABLE_DBS).borrow(); - let Some(table_db) = table_dbs.get(&id) else { - return 0; - }; - let Ok(cc) = table_db.clone().get_column_count() else { - return 0; - }; - cc -} - -fn get_table_db(id: u32, func_name: &str) -> APIResult { - let table_dbs = (*TABLE_DBS).borrow(); - let Some(table_db) = table_dbs.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( - func_name, "id", id, - )); - }; - Ok(table_db.clone()) -} - -#[wasm_bindgen()] -pub fn table_db_get_keys(id: u32, col: u32) -> Promise { - wrap_api_future_json(async move { - let table_db = get_table_db(id, "table_db_get_keys")?; - - let keys = table_db.clone().get_keys(col).await?; - let out: Vec = keys - .into_iter() - .map(|k| data_encoding::BASE64URL_NOPAD.encode(&k)) - .collect(); - APIResult::Ok(out) - }) -} - -fn add_table_db_transaction(tdbt: veilid_core::TableDBTransaction) -> u32 { - let mut next_id: u32 = 1; - let mut tdbts = (*TABLE_DB_TRANSACTIONS).borrow_mut(); - while tdbts.contains_key(&next_id) { - next_id += 1; - } - tdbts.insert(next_id, tdbt); - next_id -} - -#[wasm_bindgen()] -#[must_use] -pub fn table_db_transact(id: u32) -> u32 { - let table_dbs = (*TABLE_DBS).borrow(); - let Some(table_db) = table_dbs.get(&id) else { - return 0; - }; - let tdbt = table_db.clone().transact(); - add_table_db_transaction(tdbt) -} - -#[wasm_bindgen()] -#[must_use] -pub fn release_table_db_transaction(id: u32) -> i32 { - let mut tdbts = (*TABLE_DB_TRANSACTIONS).borrow_mut(); - if tdbts.remove(&id).is_none() { - return 0; - } - 1 -} - -fn get_table_db_transaction( - id: u32, - func_name: &str, -) -> APIResult { - let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); - let Some(tdbt) = tdbts.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( - func_name, "id", id, - )); - }; - Ok(tdbt.clone()) -} - -#[wasm_bindgen()] -pub fn table_db_transaction_commit(id: u32) -> Promise { - wrap_api_future_void(async move { - let tdbt = get_table_db_transaction(id, "table_db_transaction_commit")?; - - tdbt.commit().await?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn table_db_transaction_rollback(id: u32) -> Promise { - wrap_api_future_void(async move { - let tdbt = get_table_db_transaction(id, "table_db_transaction_rollback")?; - - tdbt.rollback(); - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn table_db_transaction_store(id: u32, col: u32, key: String, value: String) -> Promise { - wrap_api_future_void(async move { - let key: Vec = data_encoding::BASE64URL_NOPAD - .decode(key.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let value: Vec = data_encoding::BASE64URL_NOPAD - .decode(value.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let tdbt = get_table_db_transaction(id, "table_db_transaction_store")?; - - tdbt.store(col, &key, &value)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn table_db_transaction_delete(id: u32, col: u32, key: String) -> Promise { - wrap_api_future_void(async move { - let key: Vec = data_encoding::BASE64URL_NOPAD - .decode(key.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let tdbt = get_table_db_transaction(id, "table_db_transaction_delete")?; - - tdbt.delete(col, &key)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn table_db_store(id: u32, col: u32, key: String, value: String) -> Promise { - wrap_api_future_void(async move { - let key: Vec = data_encoding::BASE64URL_NOPAD - .decode(key.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let value: Vec = data_encoding::BASE64URL_NOPAD - .decode(value.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let table_db = get_table_db(id, "table_db_store")?; - - table_db.store(col, &key, &value).await?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn table_db_load(id: u32, col: u32, key: String) -> Promise { - wrap_api_future_plain(async move { - let key: Vec = data_encoding::BASE64URL_NOPAD - .decode(key.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let table_db = get_table_db(id, "table_db_load")?; - - let out = table_db.load(col, &key).await?; - let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x)); - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn table_db_delete(id: u32, col: u32, key: String) -> Promise { - wrap_api_future_plain(async move { - let key: Vec = data_encoding::BASE64URL_NOPAD - .decode(key.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let table_db = get_table_db(id, "table_db_delete")?; - - let out = table_db.delete(col, &key).await?; - let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x)); - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -#[must_use] -pub fn valid_crypto_kinds() -> String { - veilid_core::serialize_json( - veilid_core::VALID_CRYPTO_KINDS - .iter() - .map(|k| (*k).into()) - .collect::>(), - ) -} - -#[wasm_bindgen()] -#[must_use] -pub fn best_crypto_kind() -> u32 { - veilid_core::best_crypto_kind().into() -} - -#[wasm_bindgen()] -pub fn verify_signatures(node_ids: String, data: String, signatures: String) -> Promise { - wrap_api_future_json(async move { - let node_ids: Vec = - veilid_core::deserialize_json(&node_ids).map_err(VeilidAPIError::generic)?; - - let data: Vec = data_encoding::BASE64URL_NOPAD - .decode(data.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let typed_signatures: Vec = - veilid_core::deserialize_json(&signatures).map_err(VeilidAPIError::generic)?; - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let out = crypto.verify_signatures(&node_ids, &data, &typed_signatures)?; - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn generate_signatures(data: String, key_pairs: String) -> Promise { - wrap_api_future_json(async move { - let data: Vec = data_encoding::BASE64URL_NOPAD - .decode(data.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let key_pairs: Vec = - veilid_core::deserialize_json(&key_pairs).map_err(VeilidAPIError::generic)?; - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let out = crypto.generate_signatures(&data, &key_pairs, |k, s| { - veilid_core::Signature::new(k.kind(), s) - })?; - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn generate_key_pair(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_json(async move { - let out = veilid_core::Crypto::generate_keypair(kind)?; - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_cached_dh(kind: u32, key: String, secret: String) -> Promise { - wrap_api_future_json(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let key: veilid_core::BarePublicKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let secret: veilid_core::BareSecretKey = - veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_compute_dh(kind: u32, key: String, secret: String) -> Promise { - wrap_api_future_json(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let key: veilid_core::BarePublicKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let secret: veilid_core::BareSecretKey = - veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_generate_shared_secret( - kind: u32, - key: String, - secret: String, - domain: String, -) -> Promise { - wrap_api_future_json(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let key: veilid_core::BarePublicKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let secret: veilid_core::BareSecretKey = - veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; - let domain: Vec = data_encoding::BASE64URL_NOPAD - .decode(domain.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - 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_shared_secret", - "kind", - kind.to_string(), - ) - })?; - let out = csv.generate_shared_secret(&key, &secret, &domain)?; - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_random_bytes(kind: u32, len: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_plain(async move { - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_shared_secret_length(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_plain(async move { - 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_shared_secret_length", - "kind", - kind.to_string(), - ) - })?; - let out = csv.shared_secret_length(); - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_nonce_length(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_plain(async move { - 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_nonce_length", - "kind", - kind.to_string(), - ) - })?; - let out = csv.nonce_length(); - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_hash_digest_length(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_plain(async move { - 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_digest_length", - "kind", - kind.to_string(), - ) - })?; - let out = csv.hash_digest_length(); - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_public_key_length(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_plain(async move { - 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_public_key_length", - "kind", - kind.to_string(), - ) - })?; - let out = csv.public_key_length(); - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_secret_key_length(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_plain(async move { - 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_secret_key_length", - "kind", - kind.to_string(), - ) - })?; - let out = csv.secret_key_length(); - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_signature_length(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_plain(async move { - 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_signature_length", - "kind", - kind.to_string(), - ) - })?; - let out = csv.signature_length(); - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_default_salt_length(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_plain(async move { - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_aead_overhead(kind: u32) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_check_shared_secret(kind: u32, secret: String) -> Promise { - wrap_api_future_void(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let secret: veilid_core::BareSharedSecret = - veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; - - 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_check_shared_secret", - "kind", - kind.to_string(), - ) - })?; - csv.check_shared_secret(&secret)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn crypto_check_nonce(kind: u32, nonce: String) -> Promise { - wrap_api_future_void(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let nonce: veilid_core::BareNonce = - veilid_core::deserialize_json(&nonce).map_err(VeilidAPIError::generic)?; - - 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_check_nonce", - "kind", - kind.to_string(), - ) - })?; - csv.check_nonce(&nonce)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn crypto_check_hash_digest(kind: u32, digest: String) -> Promise { - wrap_api_future_void(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let digest: veilid_core::BareHashDigest = - veilid_core::deserialize_json(&digest).map_err(VeilidAPIError::generic)?; - - 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_check_hash_digest", - "kind", - kind.to_string(), - ) - })?; - csv.check_hash_digest(&digest)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn crypto_check_public_key(kind: u32, key: String) -> Promise { - wrap_api_future_void(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BarePublicKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - - 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_check_public_key", - "kind", - kind.to_string(), - ) - })?; - csv.check_public_key(&key)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn crypto_check_secret_key(kind: u32, key: String) -> Promise { - wrap_api_future_void(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let key: veilid_core::BareSecretKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - - 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_check_secret_key", - "kind", - kind.to_string(), - ) - })?; - csv.check_secret_key(&key)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn crypto_check_signature(kind: u32, signature: String) -> Promise { - wrap_api_future_void(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let signature: veilid_core::BareSignature = - veilid_core::deserialize_json(&signature).map_err(VeilidAPIError::generic)?; - - 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_check_signature", - "kind", - kind.to_string(), - ) - })?; - csv.check_signature(&signature)?; - APIRESULT_UNDEFINED - }) -} - -#[wasm_bindgen()] -pub fn crypto_hash_password(kind: u32, password: String, salt: String) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let password: Vec = data_encoding::BASE64URL_NOPAD - .decode(password.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let salt: Vec = data_encoding::BASE64URL_NOPAD - .decode(salt.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_verify_password(kind: u32, password: String, password_hash: String) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let password: Vec = data_encoding::BASE64URL_NOPAD - .decode(password.as_bytes()) - .map_err(VeilidAPIError::generic)?; - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_derive_shared_secret(kind: u32, password: String, salt: String) -> Promise { - wrap_api_future_json(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - let password: Vec = data_encoding::BASE64URL_NOPAD - .decode(password.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let salt: Vec = data_encoding::BASE64URL_NOPAD - .decode(salt.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_random_nonce(kind: u32) -> Promise { - wrap_api_future_json(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_random_shared_secret(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_json(async move { - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_generate_key_pair(kind: u32) -> Promise { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - wrap_api_future_json(async move { - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_generate_hash(kind: u32, data: String) -> Promise { - wrap_api_future_json(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let data: Vec = data_encoding::BASE64URL_NOPAD - .decode(data.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_validate_key_pair(kind: u32, key: String, secret: String) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let key: veilid_core::BarePublicKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let secret: veilid_core::BareSecretKey = - veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_validate_hash(kind: u32, data: String, hash: String) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let data: Vec = data_encoding::BASE64URL_NOPAD - .decode(data.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let hash: veilid_core::BareHashDigest = - veilid_core::deserialize_json(&hash).map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_distance(kind: u32, key1: String, key2: String) -> Promise { - wrap_api_future_json(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let key1: veilid_core::BareHashDigest = - veilid_core::deserialize_json(&key1).map_err(VeilidAPIError::generic)?; - let key2: veilid_core::BareHashDigest = - veilid_core::deserialize_json(&key2).map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_sign(kind: u32, key: String, secret: String, data: String) -> Promise { - wrap_api_future_json(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let key: veilid_core::BarePublicKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let secret: veilid_core::BareSecretKey = - veilid_core::deserialize_json(&secret).map_err(VeilidAPIError::generic)?; - - let data: Vec = data_encoding::BASE64URL_NOPAD - .decode(data.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_verify(kind: u32, key: String, data: String, signature: String) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let key: veilid_core::BarePublicKey = - veilid_core::deserialize_json(&key).map_err(VeilidAPIError::generic)?; - let data: Vec = data_encoding::BASE64URL_NOPAD - .decode(data.as_bytes()) - .map_err(VeilidAPIError::generic)?; - let signature: veilid_core::BareSignature = - veilid_core::deserialize_json(&signature).map_err(VeilidAPIError::generic)?; - - 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()) - })?; - let out = csv.verify(&key, &data, &signature)?; - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -pub fn crypto_decrypt_aead( - kind: u32, - body: String, - nonce: String, - shared_secret: String, - associated_data: Option, -) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let body: Vec = data_encoding::BASE64URL_NOPAD - .decode(body.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let nonce: veilid_core::BareNonce = - veilid_core::deserialize_json(&nonce).map_err(VeilidAPIError::generic)?; - - let shared_secret: veilid_core::BareSharedSecret = - veilid_core::deserialize_json(&shared_secret).map_err(VeilidAPIError::generic)?; - - let associated_data: Option> = match associated_data { - Some(ad) => Some( - data_encoding::BASE64URL_NOPAD - .decode(ad.as_bytes()) - .map_err(VeilidAPIError::generic)?, - ), - None => None, - }; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_encrypt_aead( - kind: u32, - body: String, - nonce: String, - shared_secret: String, - associated_data: Option, -) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let body: Vec = data_encoding::BASE64URL_NOPAD - .decode(body.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let nonce: veilid_core::BareNonce = - veilid_core::deserialize_json(&nonce).map_err(VeilidAPIError::generic)?; - - let shared_secret: veilid_core::BareSharedSecret = - veilid_core::deserialize_json(&shared_secret).map_err(VeilidAPIError::generic)?; - - let associated_data: Option> = match associated_data { - Some(ad) => Some( - data_encoding::BASE64URL_NOPAD - .decode(ad.as_bytes()) - .map_err(VeilidAPIError::generic)?, - ), - None => None, - }; - - 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) - }) -} - -#[wasm_bindgen()] -pub fn crypto_crypt_no_auth( - kind: u32, - body: String, - nonce: String, - shared_secret: String, -) -> Promise { - wrap_api_future_plain(async move { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from(kind); - - let mut body: Vec = data_encoding::BASE64URL_NOPAD - .decode(body.as_bytes()) - .map_err(VeilidAPIError::generic)?; - - let nonce: veilid_core::BareNonce = - veilid_core::deserialize_json(&nonce).map_err(VeilidAPIError::generic)?; - - let shared_secret: veilid_core::BareSharedSecret = - veilid_core::deserialize_json(&shared_secret).map_err(VeilidAPIError::generic)?; - - 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) - }) -} - -#[wasm_bindgen()] -#[must_use] -pub fn now() -> String { - veilid_core::Timestamp::now().as_u64().to_string() -} - -#[wasm_bindgen()] -pub fn debug(command: String) -> Promise { - wrap_api_future_plain(async move { - let veilid_api = get_veilid_api()?; - let out = veilid_api.debug(command).await?; - APIResult::Ok(out) - }) -} - -#[wasm_bindgen()] -#[must_use] -pub fn veilid_version_string() -> String { - veilid_core::veilid_version_string() -} - -#[derive(Serialize)] -#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] -#[tsify(into_wasm_abi)] -pub struct VeilidVersion { - pub major: u32, - pub minor: u32, - pub patch: u32, -} - -#[wasm_bindgen()] -#[must_use] -pub fn veilid_version() -> JsValue { - let (major, minor, patch) = veilid_core::veilid_version(); - let vv = VeilidVersion { - major, - minor, - patch, - }; - ::from_serde(&vv).unwrap() -} - -#[wasm_bindgen()] -#[must_use] -pub fn default_veilid_config() -> String { - veilid_core::default_veilid_config() -} diff --git a/veilid-wasm/src/veilid_crypto_js.rs b/veilid-wasm/src/veilid_crypto_js.rs deleted file mode 100644 index 4dd13e66..00000000 --- a/veilid-wasm/src/veilid_crypto_js.rs +++ /dev/null @@ -1,746 +0,0 @@ -#![allow(non_snake_case)] -use super::*; - -#[wasm_bindgen(js_name = veilidCrypto)] -pub struct VeilidCrypto {} - -// Since this implementation doesn't contain a `new` fn that's marked as a constructor, -// and none of the member fns take a &self arg, -// this is just a namespace/class of static functions. -#[wasm_bindgen(js_class = veilidCrypto)] -impl VeilidCrypto { - #[must_use] - pub fn validCryptoKinds() -> StringArray { - let res = veilid_core::VALID_CRYPTO_KINDS - .iter() - .map(|k| (*k).to_string()) - .collect(); - into_unchecked_string_array(res) - } - - #[must_use] - pub fn bestCryptoKind() -> String { - veilid_core::best_crypto_kind().to_string() - } - - pub fn cachedDh( - kind: String, - key: BarePublicKey, - secret: BareSecretKey, - ) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_cached_dh", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.cached_dh(&key, &secret)?; - APIResult::Ok(out) - } - - pub fn computeDh( - kind: String, - key: BarePublicKey, - secret: BareSecretKey, - ) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_compute_dh", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.compute_dh(&key, &secret)?; - APIResult::Ok(out) - } - - pub fn generateSharedSecret( - kind: String, - key: BarePublicKey, - secret: BareSecretKey, - domain: Box<[u8]>, - ) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_generate_shared_secret", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.generate_shared_secret(&key, &secret, &domain)?; - APIResult::Ok(out) - } - - pub fn randomBytes(kind: String, len: u32) -> APIResult> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_random_bytes", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.random_bytes(len); - let out = out.into_boxed_slice(); - APIResult::Ok(out) - } - - pub fn sharedSecretLength(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_shared_secret_length", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.shared_secret_length(); - APIResult::Ok(out) - } - - pub fn nonceLength(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_nonce_length", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.nonce_length(); - APIResult::Ok(out) - } - - pub fn hashDigestLength(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_hash_digest_length", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.hash_digest_length(); - APIResult::Ok(out) - } - - pub fn publicKeyLength(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_public_key_length", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.public_key_length(); - APIResult::Ok(out) - } - - pub fn secretKeyLength(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_secret_key_length", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.secret_key_length(); - APIResult::Ok(out) - } - - pub fn signatureLength(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_signature_length", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.signature_length(); - APIResult::Ok(out) - } - - pub fn defaultSaltLength(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_default_salt_length", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.default_salt_length(); - APIResult::Ok(out) - } - - pub fn aeadOverhead(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_aead_overhead", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.aead_overhead(); - APIResult::Ok(out) - } - - pub fn checkSharedSecret(kind: String, secret: String) -> APIResult<()> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - let secret = veilid_core::BareSharedSecret::from_str(&secret)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_check_shared_secret", - "kind", - kind.to_string(), - ) - })?; - crypto_system.check_shared_secret(&secret)?; - APIRESULT_UNDEFINED - } - - pub fn checkNonce(kind: String, nonce: String) -> APIResult<()> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - let nonce = veilid_core::BareNonce::from_str(&nonce)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_check_nonce", - "kind", - kind.to_string(), - ) - })?; - crypto_system.check_nonce(&nonce)?; - APIRESULT_UNDEFINED - } - - pub fn checkHashDigest(kind: String, digest: String) -> APIResult<()> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - let digest = veilid_core::BareHashDigest::from_str(&digest)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_check_hash_digest", - "kind", - kind.to_string(), - ) - })?; - crypto_system.check_hash_digest(&digest)?; - APIRESULT_UNDEFINED - } - - pub fn checkPublicKey(kind: String, key: String) -> APIResult<()> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - let key = veilid_core::BarePublicKey::from_str(&key)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_check_public_key", - "kind", - kind.to_string(), - ) - })?; - crypto_system.check_public_key(&key)?; - APIRESULT_UNDEFINED - } - - pub fn checkSecretKey(kind: String, key: String) -> APIResult<()> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - let key = veilid_core::BareSecretKey::from_str(&key)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_check_secret_key", - "kind", - kind.to_string(), - ) - })?; - crypto_system.check_secret_key(&key)?; - APIRESULT_UNDEFINED - } - - pub fn checkSignature(kind: String, signature: String) -> APIResult<()> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - let signature = veilid_core::BareSignature::from_str(&signature)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_check_signature", - "kind", - kind.to_string(), - ) - })?; - crypto_system.check_signature(&signature)?; - APIRESULT_UNDEFINED - } - - pub fn hashPassword(kind: String, password: Box<[u8]>, salt: Box<[u8]>) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_hash_password", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.hash_password(&password, &salt)?; - APIResult::Ok(out) - } - - pub fn verifyPassword( - kind: String, - password: Box<[u8]>, - password_hash: String, - ) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_verify_password", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.verify_password(&password, &password_hash)?; - APIResult::Ok(out) - } - - pub fn deriveSharedSecret( - kind: String, - password: Box<[u8]>, - salt: Box<[u8]>, - ) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_derive_shared_secret", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.derive_shared_secret(&password, &salt)?; - APIResult::Ok(out) - } - - pub fn randomNonce(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_random_nonce", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.random_nonce(); - APIResult::Ok(out) - } - - pub fn randomSharedSecret(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_random_shared_secret", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.random_shared_secret(); - APIResult::Ok(out) - } - - pub fn verifySignatures( - node_ids: StringArray, - data: Box<[u8]>, - signatures: StringArray, - ) -> VeilidAPIResult> { - let node_ids = into_unchecked_string_vec(node_ids); - let node_ids: Vec = node_ids - .iter() - .map(|k| { - veilid_core::PublicKey::from_str(k).map_err(|e| { - VeilidAPIError::invalid_argument( - "verifySignatures()", - format!("error decoding nodeid in node_ids[]: {}", e), - k, - ) - }) - }) - .collect::>>()?; - - let typed_signatures = into_unchecked_string_vec(signatures); - let typed_signatures: Vec = typed_signatures - .iter() - .map(|k| { - Signature::from_str(k).map_err(|e| { - VeilidAPIError::invalid_argument( - "verifySignatures()", - format!("error decoding keypair in key_pairs[]: {}", e), - k, - ) - }) - }) - .collect::>>()?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let out = crypto - .verify_signatures(&node_ids, &data, &typed_signatures)? - .map(|sigs| { - let out = sigs - .iter() - .map(|item| item.to_string()) - .collect::>(); - into_unchecked_string_array(out) - }); - APIResult::Ok(out) - } - - pub fn generateSignatures(data: Box<[u8]>, key_pairs: StringArray) -> APIResult { - let key_pairs = into_unchecked_string_vec(key_pairs); - let key_pairs: Vec = key_pairs - .iter() - .map(|k| { - veilid_core::KeyPair::from_str(k).map_err(|e| { - VeilidAPIError::invalid_argument( - "generateSignatures()", - format!("error decoding keypair in key_pairs[]: {}", e), - k, - ) - }) - }) - .collect::>>()?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let out = crypto.generate_signatures(&data, &key_pairs, |k, s| { - veilid_core::Signature::new(k.kind(), s).to_string() - })?; - let out = into_unchecked_string_array(out); - APIResult::Ok(out) - } - - pub fn generateKeyPair(kind: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_generate_key_pair", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.generate_keypair(); - let out = out.encode(); - APIResult::Ok(out) - } - - pub fn generateHash(kind: String, data: Box<[u8]>) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_generate_hash", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.generate_hash(&data); - APIResult::Ok(out) - } - - pub fn validateKeyPair(kind: String, key: String, secret: String) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let key: veilid_core::BarePublicKey = veilid_core::BarePublicKey::from_str(&key)?; - let secret: veilid_core::BareSecretKey = veilid_core::BareSecretKey::from_str(&secret)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_validate_key_pair", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.validate_keypair(&key, &secret); - APIResult::Ok(out) - } - - pub fn validateHash(kind: String, data: Box<[u8]>, hash: BareHashDigest) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_validate_hash", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.validate_hash(&data, &hash); - APIResult::Ok(out) - } - - pub fn distance( - kind: String, - hash1: BareHashDigest, - hash2: BareHashDigest, - ) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_distance", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.distance(&hash1, &hash2); - APIResult::Ok(out) - } - - pub fn sign( - kind: String, - key: String, - secret: String, - data: Box<[u8]>, - ) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let key: veilid_core::BarePublicKey = veilid_core::BarePublicKey::from_str(&key)?; - let secret: veilid_core::BareSecretKey = veilid_core::BareSecretKey::from_str(&secret)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument("crypto_sign", "kind", kind.to_string()) - })?; - let out = crypto_system.sign(&key, &secret, &data)?; - APIResult::Ok(out) - } - - pub fn verify( - kind: String, - key: BarePublicKey, - data: Box<[u8]>, - signature: BareSignature, - ) -> APIResult { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string()) - })?; - let out = crypto_system.verify(&key, &data, &signature)?; - APIResult::Ok(out) - } - - pub fn decryptAead( - kind: String, - body: Box<[u8]>, - nonce: BareNonce, - shared_secret: BareSharedSecret, - associated_data: Option>, - ) -> APIResult> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_decrypt_aead", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.decrypt_aead( - &body, - &nonce, - &shared_secret, - match &associated_data { - Some(ad) => Some(ad), - None => None, - }, - )?; - let out = out.into_boxed_slice(); - APIResult::Ok(out) - } - - pub fn encryptAead( - kind: String, - body: Box<[u8]>, - nonce: BareNonce, - shared_secret: BareSharedSecret, - associated_data: Option>, - ) -> APIResult> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_encrypt_aead", - "kind", - kind.to_string(), - ) - })?; - let out = crypto_system.encrypt_aead( - &body, - &nonce, - &shared_secret, - match &associated_data { - Some(ad) => Some(ad), - None => None, - }, - )?; - APIResult::Ok(out.into_boxed_slice()) - } - - pub fn cryptNoAuth( - kind: String, - mut body: Box<[u8]>, - nonce: BareNonce, - shared_secret: BareSharedSecret, - ) -> APIResult> { - let kind: veilid_core::CryptoKind = veilid_core::CryptoKind::from_str(&kind)?; - - let veilid_api = get_veilid_api()?; - let crypto = veilid_api.crypto()?; - let crypto_system = crypto.get(kind).ok_or_else(|| { - veilid_core::VeilidAPIError::invalid_argument( - "crypto_crypt_no_auth", - "kind", - kind.to_string(), - ) - })?; - crypto_system.crypt_in_place_no_auth(&mut body, &nonce, &shared_secret)?; - APIResult::Ok(body) - } - - // -------------------------------- - // Constants - // (written as getters since wasm_bindgen doesn't support export of const) - // -------------------------------- - - /// Length of a VLD0 hash digest in bytes - #[wasm_bindgen(getter)] - #[must_use] - pub fn VLD0_HASH_DIGEST_LENGTH() -> usize { - veilid_core::VLD0_HASH_DIGEST_LENGTH - } - - /// Length of a VLD0 nonce in bytes - #[wasm_bindgen(getter)] - #[must_use] - pub fn VLD0_NONCE_LENGTH() -> usize { - veilid_core::VLD0_NONCE_LENGTH - } - - /// Length of a VLD0 public key in bytes - #[wasm_bindgen(getter)] - #[must_use] - pub fn VLD0_PUBLIC_KEY_LENGTH() -> usize { - veilid_core::VLD0_PUBLIC_KEY_LENGTH - } - - /// Length of a VLD0 secret key in bytes - #[wasm_bindgen(getter)] - #[must_use] - pub fn VLD0_SECRET_KEY_LENGTH() -> usize { - veilid_core::VLD0_SECRET_KEY_LENGTH - } - - /// Length of a VLD0 shared secret in bytes - #[wasm_bindgen(getter)] - #[must_use] - pub fn VLD0_SHARED_SECRET_LENGTH() -> usize { - veilid_core::VLD0_SHARED_SECRET_LENGTH - } - - /// Length of a VLD0 signature in bytes - #[wasm_bindgen(getter)] - #[must_use] - pub fn VLD0_SIGNATURE_LENGTH() -> usize { - veilid_core::VLD0_SIGNATURE_LENGTH - } -} diff --git a/veilid-wasm/src/veilid_version.rs b/veilid-wasm/src/veilid_version.rs new file mode 100644 index 00000000..28fb52ff --- /dev/null +++ b/veilid-wasm/src/veilid_version.rs @@ -0,0 +1,10 @@ +use super::*; + +#[derive(Debug, Clone, Serialize)] +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +#[tsify(into_wasm_abi)] +pub struct VeilidVersion { + pub major: u32, + pub minor: u32, + pub patch: u32, +} diff --git a/veilid-wasm/src/veilid_wasm_config.rs b/veilid-wasm/src/veilid_wasm_config.rs new file mode 100644 index 00000000..547bcbcf --- /dev/null +++ b/veilid-wasm/src/veilid_wasm_config.rs @@ -0,0 +1,36 @@ +use super::*; + +#[derive(Debug, Deserialize, Serialize)] +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +pub struct VeilidWASMConfigLoggingPerformance { + pub enabled: bool, + pub level: veilid_core::VeilidConfigLogLevel, + pub logs_in_timings: bool, + pub logs_in_console: bool, + pub ignore_log_targets: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +pub struct VeilidWASMConfigLoggingAPI { + pub enabled: bool, + pub level: veilid_core::VeilidConfigLogLevel, + pub ignore_log_targets: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))] +pub struct VeilidWASMConfigLogging { + pub performance: VeilidWASMConfigLoggingPerformance, + pub api: VeilidWASMConfigLoggingAPI, +} + +#[derive(Debug, Deserialize, Serialize)] +#[cfg_attr( + all(target_arch = "wasm32", target_os = "unknown"), + derive(Tsify), + tsify(from_wasm_abi) +)] +pub struct VeilidWASMConfig { + pub logging: VeilidWASMConfigLogging, +} diff --git a/veilid-wasm/src/wasm_helpers.rs b/veilid-wasm/src/wasm_helpers.rs index 7538ae96..6209d4c8 100644 --- a/veilid-wasm/src/wasm_helpers.rs +++ b/veilid-wasm/src/wasm_helpers.rs @@ -1,18 +1,48 @@ use super::*; -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "string[]")] - pub type StringArray; +// JSON Helpers for WASM +#[allow(dead_code)] +pub fn to_json(val: T) -> JsValue { + JsValue::from_str(&serialize_json(val)) } -/// Convert a `Vec` into a `js_sys::Array` with the type of `string[]` -pub(crate) fn into_unchecked_string_array(items: Vec) -> StringArray { - items - .iter() - .map(JsValue::from) - .collect::() - .unchecked_into::() // TODO: can I do this a better way? +pub fn to_jsvalue(val: T) -> JsValue +where + JsValue: From, +{ + JsValue::from(val) +} + +#[expect(dead_code)] +pub fn from_json( + val: JsValue, +) -> Result { + let s = val + .as_string() + .ok_or_else(|| veilid_core::VeilidAPIError::ParseError { + message: "Value is not String".to_owned(), + value: String::new(), + })?; + deserialize_json(&s) +} + +// Marshalling helpers +#[expect(dead_code)] +pub fn unmarshall(b64: String) -> APIResult> { + data_encoding::BASE64URL_NOPAD + .decode(b64.as_bytes()) + .map_err(|e| { + VeilidAPIError::generic(format!( + "error decoding base64url string '{}' into bytes: {}", + b64, e + )) + }) +} + +#[expect(dead_code)] +#[must_use] +pub fn marshall(data: &[u8]) -> String { + data_encoding::BASE64URL_NOPAD.encode(data) } #[wasm_bindgen] @@ -20,20 +50,12 @@ extern "C" { #[wasm_bindgen(typescript_type = "Uint8Array[]")] pub type Uint8ArrayArray; } + /// Convert a `Vec` into a `js_sys::Array` with the type of `Uint8Array[]` +#[allow(dead_code)] pub(crate) fn into_unchecked_uint8array_array(items: Vec) -> Uint8ArrayArray { items .iter() .collect::() .unchecked_into::() // TODO: can I do this a better way? } - -/// Convert a StringArray (`js_sys::Array` with the type of `string[]`) into `Vec` -pub(crate) fn into_unchecked_string_vec(items: StringArray) -> Vec { - items - .unchecked_into::() - .to_vec() - .into_iter() - .map(|i| serde_wasm_bindgen::from_value(i).unwrap()) - .collect::>() -} diff --git a/veilid-wasm/tests/src/VeilidRoutingContext.test.ts b/veilid-wasm/tests/src/VeilidRoutingContext.test.ts index f60d17fa..e12dba7b 100644 --- a/veilid-wasm/tests/src/VeilidRoutingContext.test.ts +++ b/veilid-wasm/tests/src/VeilidRoutingContext.test.ts @@ -82,258 +82,263 @@ describe('VeilidRoutingContext', () => { routingContext = VeilidRoutingContext.create(); }); - describe('createDhtRecord', () => { - it('should create dht record with default schema', async () => { - const dhtRecord = await routingContext.createDhtRecord({ kind: 'DFLT', o_cnt: 1 }); - expect(dhtRecord.key).toBeDefined(); - expect(dhtRecord.owner).toBeDefined(); - expect(dhtRecord.owner_secret).toBeDefined(); - expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 }); - }); + for (const cryptoKind of veilidCrypto.VALID_CRYPTO_KINDS) { - it('should create dht record with default schema, no owner', async () => { - const dhtRecord = await routingContext.createDhtRecord({ kind: 'DFLT', o_cnt: 1 }, undefined, veilidCrypto.bestCryptoKind()); - expect(dhtRecord.key).toBeDefined(); - expect(dhtRecord.owner).toBeDefined(); - expect(dhtRecord.owner_secret).toBeDefined(); - expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 }); - }); + describe(`createDhtRecord for ${cryptoKind}`, () => { + it('should create dht record with default schema', async () => { + const dhtRecord = await routingContext.createDhtRecord(cryptoKind, { kind: 'DFLT', o_cnt: 1 }); + expect(dhtRecord.key).toBeDefined(); + expect(dhtRecord.owner).toBeDefined(); + expect(dhtRecord.owner_secret).toBeDefined(); + expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 }); + }); - it('should create dht record with default schema, with owner, and a deterministic key', async () => { - const bestCryptoKind = veilidCrypto.bestCryptoKind(); - const ownerKeyPair = veilidCrypto.generateKeyPair(bestCryptoKind); - const [owner, secret] = ownerKeyPair.split(':'); - const dhtRecordKey = await routingContext.getDhtRecordKey({ kind: 'DFLT', o_cnt: 1 }, `${bestCryptoKind}:${owner}`); - const dhtRecord = await routingContext.createDhtRecord({ kind: 'DFLT', o_cnt: 1 }, ownerKeyPair, bestCryptoKind); - expect(dhtRecord.key).toBeDefined(); - expect(dhtRecord.key).toEqual(dhtRecordKey); - expect(dhtRecord.owner).toBeDefined(); - expect(dhtRecord.owner).toEqual(`${bestCryptoKind}:${owner}`); - expect(dhtRecord.owner_secret).toBeDefined(); - expect(dhtRecord.owner_secret).toEqual(secret); - expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 }); - }); - }); + it('should create dht record with default schema, no owner for', async () => { + const dhtRecord = await routingContext.createDhtRecord(cryptoKind, { kind: 'DFLT', o_cnt: 1 }, undefined); + expect(dhtRecord.key).toBeDefined(); + expect(dhtRecord.owner).toBeDefined(); + expect(dhtRecord.owner_secret).toBeDefined(); + expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 }); + }); - describe('DHT kitchen sink', () => { - let dhtRecord: DHTRecordDescriptor; - const data = 'πŸš€ This example DHT data with unicode a Δ€ 𐀀 ζ–‡ πŸš€'; - - beforeEach('create dht record', async () => { - dhtRecord = await routingContext.createDhtRecord( - { - kind: 'DFLT', - o_cnt: 1, - }, - ); - - expect(dhtRecord.key).toBeDefined(); - expect(dhtRecord.owner).toBeDefined(); - expect(dhtRecord.owner_secret).toBeDefined(); - expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 }); - }); - - afterEach('free dht record', async () => { - await routingContext.deleteDhtRecord(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 setValueRes = await routingContext.setDhtValue( - dhtRecord.key, - 0, - textEncoder.encode(data) - ); - expect(setValueRes).toBeUndefined(); - - // Wait for synchronization - await waitForOfflineSubkeyWrite(routingContext, dhtRecord.key); - - 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 create dht record with default schema, with owner, and a deterministic key', async () => { + const vcrypto = veilidClient.getCrypto(cryptoKind) + const ownerKeyPair = vcrypto.generateKeyPair(); + const owner = ownerKeyPair.key + const secret = ownerKeyPair.secret + const dhtRecordKey = await routingContext.getDhtRecordKey({ kind: 'DFLT', o_cnt: 1 }, owner); + const dhtRecord = await routingContext.createDhtRecord(cryptoKind, { kind: 'DFLT', o_cnt: 1 }, ownerKeyPair); + expect(dhtRecord.key).toBeDefined(); + expect(dhtRecord.key.isEqual(dhtRecordKey)).toEqual(true); + expect(dhtRecord.owner).toBeDefined(); + expect(dhtRecord.owner.isEqual(owner)).toEqual(true); + expect(dhtRecord.owner_secret).toBeDefined(); + expect(dhtRecord.owner_secret?.isEqual(secret)).toEqual(true); + expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 }); }); }); - it('should open writable record', async () => { - await routingContext.closeDhtRecord(dhtRecord.key); + describe(`DHT kitchen sink for ${cryptoKind}`, () => { + let dhtRecord: DHTRecordDescriptor; + const data = 'πŸš€ This example DHT data with unicode a Δ€ 𐀀 ζ–‡ πŸš€'; - 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(); - }); + beforeEach('create dht record', async () => { + dhtRecord = await routingContext.createDhtRecord( + cryptoKind, + { + kind: 'DFLT', + o_cnt: 1, + }, + ); - it('should open readonly record and specify writer during the set', async () => { - await routingContext.closeDhtRecord(dhtRecord.key); - - const writeableDhtRecord = await routingContext.openDhtRecord( - dhtRecord.key, - ); - expect(writeableDhtRecord).toBeDefined(); - const setValueResFail = routingContext.setDhtValue( - dhtRecord.key, - 0, - textEncoder.encode(`${data}πŸ‘‹`), - ); - await expect(setValueResFail).rejects.toEqual({ - kind: 'Generic', - message: 'value is not writable', + expect(dhtRecord.key).toBeDefined(); + expect(dhtRecord.owner).toBeDefined(); + expect(dhtRecord.owner_secret).toBeDefined(); + expect(dhtRecord.schema).toEqual({ kind: 'DFLT', o_cnt: 1 }); + }); + + afterEach('free dht record', async () => { + await routingContext.deleteDhtRecord(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 setValueRes = await routingContext.setDhtValue( + dhtRecord.key, + 0, + textEncoder.encode(data) + ); + expect(setValueRes).toBeUndefined(); + + // Wait for synchronization + await waitForOfflineSubkeyWrite(routingContext, dhtRecord.key); + + const getValueRes = await routingContext.getDhtValue( + dhtRecord.key, + 0, + true + ); + expect(getValueRes?.data).toBeDefined(); + expect(textDecoder.decode(getValueRes?.data)).toBe(data); + + expect(getValueRes?.writer.isEqual(dhtRecord.owner)).toEqual(true); + 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_keypair + ); + expect(writeableDhtRecord).toBeDefined(); + const setValueRes = await routingContext.setDhtValue( + dhtRecord.key, + 0, + textEncoder.encode(`${data}πŸ‘‹`) + ); + expect(setValueRes).toBeUndefined(); + }); + + it('should open readonly record and specify writer during the set', async () => { + await routingContext.closeDhtRecord(dhtRecord.key); + + const writeableDhtRecord = await routingContext.openDhtRecord( + dhtRecord.key, + ); + expect(writeableDhtRecord).toBeDefined(); + const setValueResFail = routingContext.setDhtValue( + dhtRecord.key, + 0, + textEncoder.encode(`${data}πŸ‘‹`), + ); + await expect(setValueResFail).rejects.toEqual({ + kind: 'Generic', + message: 'value is not writable', + }); + const setValueRes = await routingContext.setDhtValue( + dhtRecord.key, + 0, + textEncoder.encode(`${data}πŸ‘‹`), + { + writer: dhtRecord.owner_keypair, + allow_offline: undefined + } + ); + expect(setValueRes).toBeUndefined(); + }); + + it('should watch value and cancel watch', async () => { + const setValueRes = await routingContext.setDhtValue( + dhtRecord.key, + 0, + textEncoder.encode(data) + ); + expect(setValueRes).toBeUndefined(); + + // With typical values + const watchValueRes = await routingContext.watchDhtValues( + dhtRecord.key, + [[0, 0]], + "0", + 0xFFFFFFFF, + ); + expect(watchValueRes).toEqual(true); + + const cancelValueRes = await routingContext.cancelDhtWatch( + dhtRecord.key, + [], + ) + + expect(cancelValueRes).toEqual(false); + + }); + + it('should watch value and cancel watch with default values', async () => { + const setValueRes = await routingContext.setDhtValue( + dhtRecord.key, + 0, + textEncoder.encode(data) + ); + expect(setValueRes).toBeUndefined(); + + // Again with default values + const watchValueRes = await routingContext.watchDhtValues( + dhtRecord.key, + ); + expect(watchValueRes).toEqual(true); + + const cancelValueRes = await routingContext.cancelDhtWatch( + dhtRecord.key, + ) + expect(cancelValueRes).toEqual(false); + }); + + it('should set a value and inspect it', async () => { + const setValueRes = await routingContext.setDhtValue( + dhtRecord.key, + 0, + textEncoder.encode(data) + ); + expect(setValueRes).toBeUndefined(); + + // Inspect locally + const inspectRes = await routingContext.inspectDhtRecord( + dhtRecord.key, + [[0, 0]], + "Local", + ); + expect(inspectRes).toBeDefined(); + expect(inspectRes.subkeys).toEqual([[0, 0]]); + expect(inspectRes.local_seqs).toEqual([0]); + expect(inspectRes.network_seqs).toEqual([undefined]); + + // Wait for synchronization + await waitForOfflineSubkeyWrite(routingContext, dhtRecord.key); + + // Inspect network + const inspectRes2 = await routingContext.inspectDhtRecord( + dhtRecord.key, + [[0, 0]], + "SyncGet", + ); + expect(inspectRes2).toBeDefined(); + expect(inspectRes.subkeys).toEqual([[0, 0]]); + expect(inspectRes.offline_subkeys).toEqual([]); + expect(inspectRes2.local_seqs).toEqual([0]); + expect(inspectRes2.network_seqs).toEqual([0]); + }); + + it('should set a value and inspect it with defaults', async () => { + const setValueRes = await routingContext.setDhtValue( + dhtRecord.key, + 0, + textEncoder.encode(data) + ); + expect(setValueRes).toBeUndefined(); + + // Wait for synchronization + await waitForOfflineSubkeyWrite(routingContext, dhtRecord.key); + + // Inspect locally + const inspectRes = await routingContext.inspectDhtRecord( + dhtRecord.key, + ); + expect(inspectRes).toBeDefined(); + expect(inspectRes.offline_subkeys).toEqual([]); + expect(inspectRes.local_seqs).toEqual([0]); + expect(inspectRes.network_seqs).toEqual([undefined]); }); - const setValueRes = await routingContext.setDhtValue( - dhtRecord.key, - 0, - textEncoder.encode(`${data}πŸ‘‹`), - { - writer: `${dhtRecord.owner}:${dhtRecord.owner_secret}`, - allow_offline: undefined - } - ); - expect(setValueRes).toBeUndefined(); }); - - it('should watch value and cancel watch', async () => { - const setValueRes = await routingContext.setDhtValue( - dhtRecord.key, - 0, - textEncoder.encode(data) - ); - expect(setValueRes).toBeUndefined(); - - // With typical values - const watchValueRes = await routingContext.watchDhtValues( - dhtRecord.key, - [[0, 0]], - "0", - 0xFFFFFFFF, - ); - expect(watchValueRes).toEqual(true); - - const cancelValueRes = await routingContext.cancelDhtWatch( - dhtRecord.key, - [], - ) - - expect(cancelValueRes).toEqual(false); - - }); - - it('should watch value and cancel watch with default values', async () => { - const setValueRes = await routingContext.setDhtValue( - dhtRecord.key, - 0, - textEncoder.encode(data) - ); - expect(setValueRes).toBeUndefined(); - - // Again with default values - const watchValueRes = await routingContext.watchDhtValues( - dhtRecord.key, - ); - expect(watchValueRes).toEqual(true); - - const cancelValueRes = await routingContext.cancelDhtWatch( - dhtRecord.key, - ) - expect(cancelValueRes).toEqual(false); - }); - - it('should set a value and inspect it', async () => { - const setValueRes = await routingContext.setDhtValue( - dhtRecord.key, - 0, - textEncoder.encode(data) - ); - expect(setValueRes).toBeUndefined(); - - // Inspect locally - const inspectRes = await routingContext.inspectDhtRecord( - dhtRecord.key, - [[0, 0]], - "Local", - ); - expect(inspectRes).toBeDefined(); - expect(inspectRes.subkeys).toEqual([[0, 0]]); - expect(inspectRes.local_seqs).toEqual([0]); - expect(inspectRes.network_seqs).toEqual([undefined]); - - // Wait for synchronization - await waitForOfflineSubkeyWrite(routingContext, dhtRecord.key); - - // Inspect network - const inspectRes2 = await routingContext.inspectDhtRecord( - dhtRecord.key, - [[0, 0]], - "SyncGet", - ); - expect(inspectRes2).toBeDefined(); - expect(inspectRes.subkeys).toEqual([[0, 0]]); - expect(inspectRes.offline_subkeys).toEqual([]); - expect(inspectRes2.local_seqs).toEqual([0]); - expect(inspectRes2.network_seqs).toEqual([0]); - }); - - it('should set a value and inspect it with defaults', async () => { - const setValueRes = await routingContext.setDhtValue( - dhtRecord.key, - 0, - textEncoder.encode(data) - ); - expect(setValueRes).toBeUndefined(); - - // Wait for synchronization - await waitForOfflineSubkeyWrite(routingContext, dhtRecord.key); - - // Inspect locally - const inspectRes = await routingContext.inspectDhtRecord( - dhtRecord.key, - ); - expect(inspectRes).toBeDefined(); - expect(inspectRes.offline_subkeys).toEqual([]); - expect(inspectRes.local_seqs).toEqual([0]); - expect(inspectRes.network_seqs).toEqual([undefined]); - }); - }); + } }); }); diff --git a/veilid-wasm/tests/src/utils/veilid-config.ts b/veilid-wasm/tests/src/utils/veilid-config.ts index 4cbe6184..18c5a64f 100644 --- a/veilid-wasm/tests/src/utils/veilid-config.ts +++ b/veilid-wasm/tests/src/utils/veilid-config.ts @@ -1,5 +1,4 @@ -import type { VeilidWASMConfig } from 'veilid-wasm'; -import { veilidClient } from 'veilid-wasm'; +import { VeilidWASMConfig, veilidClient } from 'veilid-wasm'; export const veilidCoreInitConfig: VeilidWASMConfig = { logging: { @@ -19,20 +18,23 @@ export const veilidCoreInitConfig: VeilidWASMConfig = { }; export var veilidCoreStartupConfig = (() => { + // console.log("starting config") var defaultConfig = veilidClient.defaultConfig(); defaultConfig.program_name = 'veilid-wasm-test'; - if(process.env.NETWORK_KEY) { + if (process.env.NETWORK_KEY) { defaultConfig.network.network_key_password = process.env.NETWORK_KEY; } - if(process.env.BOOTSTRAP_KEYS) { - defaultConfig.network.routing_table.bootstrap_keys = process.env.BOOTSTRAP_KEYS.split(',') as `${string}:${string}`[]; + if (process.env.BOOTSTRAP_KEYS) { + defaultConfig.network.routing_table.bootstrap_keys = process.env.BOOTSTRAP_KEYS.split(',') } - if(process.env.BOOTSTRAP) { + if (process.env.BOOTSTRAP) { defaultConfig.network.routing_table.bootstrap = process.env.BOOTSTRAP.split(','); } // Ensure we are starting from scratch defaultConfig.table_store.delete = true; defaultConfig.protected_store.delete = true; defaultConfig.block_store.delete = true; + // console.log("ending config") + return defaultConfig; })(); diff --git a/veilid-wasm/tests/src/utils/wait-utils.ts b/veilid-wasm/tests/src/utils/wait-utils.ts index d491506c..00d7c9a7 100644 --- a/veilid-wasm/tests/src/utils/wait-utils.ts +++ b/veilid-wasm/tests/src/utils/wait-utils.ts @@ -1,4 +1,4 @@ -import { veilidClient, VeilidRoutingContext, PublicKey } from 'veilid-wasm'; +import { veilidClient, VeilidRoutingContext, RecordKey } from 'veilid-wasm'; export const waitForMs = (milliseconds: number) => { return new Promise((resolve) => setTimeout(resolve, milliseconds)); @@ -70,7 +70,7 @@ export const waitForShutdown = async () => { } } -export const waitForOfflineSubkeyWrite = async (routingContext: VeilidRoutingContext, key: PublicKey) => { +export const waitForOfflineSubkeyWrite = async (routingContext: VeilidRoutingContext, key: RecordKey) => { while ((await routingContext.inspectDhtRecord(key)).offline_subkeys.length != 0) { await waitForMs(200); } diff --git a/veilid-wasm/tests/src/veilidClient.test.ts b/veilid-wasm/tests/src/veilidClient.test.ts index 454bfd23..88b7dd20 100644 --- a/veilid-wasm/tests/src/veilidClient.test.ts +++ b/veilid-wasm/tests/src/veilidClient.test.ts @@ -5,20 +5,26 @@ import { veilidCoreStartupConfig, } from './utils/veilid-config'; -import { VeilidState, veilidClient } from 'veilid-wasm'; +import { VeilidState, veilidClient, veilidCrypto, KeyPair, Signature } from 'veilid-wasm'; import { asyncCallWithTimeout, waitForDetached, waitForPublicAttachment, waitForShutdown } from './utils/wait-utils'; +import { textEncoder } from './utils/marshalling-utils'; describe('veilidClient', function () { before('veilid startup', async function () { + // console.log("---Init---"); veilidClient.initializeCore(veilidCoreInitConfig); + // console.log("---Startup---"); + // console.log("config: ", veilidCoreStartupConfig); await veilidClient.startupCore(function (_update) { // if (_update.kind === 'Log') { // console.log(_update.message); // } }, veilidCoreStartupConfig); + // console.log("---Started Up---"); }); after('veilid shutdown', async function () { + // console.log("---Shutting Down---"); await veilidClient.shutdownCore(); await asyncCallWithTimeout(waitForShutdown(), 10000); }); @@ -80,4 +86,45 @@ describe('veilidClient', function () { expect(response.length).toBeGreaterThan(0); }); }); + + describe('global crypto functions', function () { + + it(`should sign and verify for all crypto kinds`, () => { + + const keypairs: KeyPair[] = [] + for (const cryptoKind of veilidCrypto.VALID_CRYPTO_KINDS) { + const vcrypto = veilidClient.getCrypto(cryptoKind) + const keypair = vcrypto.generateKeyPair(); + expect(typeof keypair).toBe('object'); + + keypairs.push(keypair); + } + + const data = textEncoder.encode( + 'This is some data I am signing with my key πŸ”‘' + ); + + let signatures: Signature[]; + expect(() => { + signatures = veilidClient.generateSignatures(data, keypairs); + expect(typeof signatures).toBe('object'); + }).not.toThrow(); + + const publicKeys = keypairs.map((kp) => kp.key) + + expect(() => { + const res = veilidClient.verifySignatures(publicKeys, data, signatures); + expect(res).not.toBeUndefined(); + expect(res!.length).toEqual(publicKeys.length); + }).not.toThrow(); + + signatures = [] + expect(() => { + const res = veilidClient.verifySignatures(publicKeys, data, signatures); + expect(res).not.toBeUndefined(); + expect(res!.length).toEqual(0); + }).not.toThrow(); + + }); + }) }); diff --git a/veilid-wasm/tests/src/veilidCrypto.test.ts b/veilid-wasm/tests/src/veilidCrypto.test.ts index f24e0f51..28429a97 100644 --- a/veilid-wasm/tests/src/veilidCrypto.test.ts +++ b/veilid-wasm/tests/src/veilidCrypto.test.ts @@ -23,143 +23,134 @@ describe('veilidCrypto', () => { }); it('should list crypto kinds', () => { - const kinds = veilidCrypto.validCryptoKinds(); - const bestKind = veilidCrypto.bestCryptoKind(); - - expect(typeof bestKind).toBe('string'); - expect(kinds.includes(bestKind)).toBe(true); + const kinds = veilidCrypto.VALID_CRYPTO_KINDS; + expect(kinds.length).toBeGreaterThan(0) }); - it('should generate key pair', () => { - const bestKind = veilidCrypto.bestCryptoKind(); - const keypair = veilidCrypto.generateKeyPair(bestKind); - expect(typeof keypair).toBe('string'); + for (const cryptoKind of veilidCrypto.VALID_CRYPTO_KINDS) { + it(`should generate key pair for ${cryptoKind}`, () => { + const vcrypto = veilidClient.getCrypto(cryptoKind) + const keypair = vcrypto.generateKeyPair(); + expect(typeof keypair).toBe('object'); - const [publicKey, secretKey] = keypair.split(':'); - expect(unmarshallBytes(publicKey).length).toBe(32); - expect(unmarshallBytes(secretKey).length).toBe(32); + const keyPairKind = keypair.kind; + const barePublicKey = keypair.value.key + const bareSecretKey = keypair.value.secret + expect(keyPairKind).toEqual(cryptoKind); + expect(unmarshallBytes(barePublicKey.toString()).length).toBe(vcrypto.publicKeyLength()); + expect(unmarshallBytes(bareSecretKey.toString()).length).toBe(vcrypto.secretKeyLength()); - const isValid = veilidCrypto.validateKeyPair( - bestKind, - publicKey, - secretKey - ); - expect(isValid).toBe(true); - }); + const isValid = vcrypto.validateKeyPair( + keypair.key, + keypair.secret, + ); + 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).toBe(true); - }).not.toThrow(); - }); - - describe('constants', () => { - it('VLD0_HASH_DIGEST_LENGTH', () => { - expect(typeof veilidCrypto.VLD0_HASH_DIGEST_LENGTH).toBe('number'); }); - it('VLD0_NONCE_LENGTH', () => { - expect(typeof veilidCrypto.VLD0_NONCE_LENGTH).toBe('number'); + } + + for (const cryptoKind of veilidCrypto.VALID_CRYPTO_KINDS) { + it(`should generate random bytes for ${cryptoKind}`, () => { + const vcrypto = veilidClient.getCrypto(cryptoKind) + const bytes = vcrypto.randomBytes(64); + expect(bytes instanceof Uint8Array).toBe(true); + expect(bytes.length).toBe(64); + }); - it('VLD0_PUBLIC_KEY_LENGTH', () => { - expect(typeof veilidCrypto.VLD0_PUBLIC_KEY_LENGTH).toBe('number'); + } + + for (const cryptoKind of veilidCrypto.VALID_CRYPTO_KINDS) { + it(`should hash data and validate hash for ${cryptoKind}`, () => { + const vcrypto = veilidClient.getCrypto(cryptoKind) + const data = textEncoder.encode('this is my dataπŸš€'); + const hash = vcrypto.generateHash(data); + + expect(hash).toBeDefined(); + expect(typeof hash).toBe('object'); + + const isValid = vcrypto.validateHash(data, hash); + expect(isValid).toBe(true); }); - it('VLD0_SECRET_KEY_LENGTH', () => { - expect(typeof veilidCrypto.VLD0_SECRET_KEY_LENGTH).toBe('number'); + } + + for (const cryptoKind of veilidCrypto.VALID_CRYPTO_KINDS) { + it(`should hash and validate password for ${cryptoKind}`, () => { + const vcrypto = veilidClient.getCrypto(cryptoKind) + + const password = textEncoder.encode('this is my dataπŸš€'); + const saltLength = vcrypto.defaultSaltLength(); + expect(saltLength).toBeGreaterThan(0); + + const salt = vcrypto.randomBytes(saltLength); + expect(salt instanceof Uint8Array).toBe(true); + expect(salt.length).toBe(saltLength); + + const hash = vcrypto.hashPassword(password, salt); + expect(hash).toBeDefined(); + expect(typeof hash).toBe('string'); + + const isValid = vcrypto.verifyPassword(password, hash); + expect(isValid).toBe(true); }); - it('VLD0_SHARED_SECRET_LENGTH', () => { - expect(typeof veilidCrypto.VLD0_SHARED_SECRET_LENGTH).toBe('number'); + } + + for (const cryptoKind of veilidCrypto.VALID_CRYPTO_KINDS) { + it(`should aead encrypt and decrypt for ${cryptoKind}`, () => { + const vcrypto = veilidClient.getCrypto(cryptoKind) + 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 = vcrypto.randomNonce(); + expect(typeof nonce).toBe('object'); + + const sharedSecred = vcrypto.randomSharedSecret(); + expect(typeof sharedSecred).toBe('object'); + + const encBody = vcrypto.encryptAead( + body, + nonce, + sharedSecred, + ad + ); + expect(encBody instanceof Uint8Array).toBe(true); + + const overhead = vcrypto.aeadOverhead(); + expect(encBody.length - body.length).toBe(overhead); + + const decBody = vcrypto.decryptAead( + encBody, + nonce, + sharedSecred, + ad + ); + expect(decBody instanceof Uint8Array).toBe(true); + expect(body).toEqual(decBody); }); - it('VLD0_SIGNATURE_LENGTH', () => { - expect(typeof veilidCrypto.VLD0_SIGNATURE_LENGTH).toBe('number'); + } + for (const cryptoKind of veilidCrypto.VALID_CRYPTO_KINDS) { + it(`should sign and verify for ${cryptoKind}`, () => { + const vcrypto = veilidClient.getCrypto(cryptoKind) + const keypair = vcrypto.generateKeyPair(); + const data = textEncoder.encode( + 'This is some data I am signing with my key πŸ”‘' + ); + expect(typeof keypair).toBe('object'); + + const publicKey = keypair.key; + const secretKey = keypair.secret; + + const sig = vcrypto.sign(publicKey, secretKey, data); + expect(typeof sig).toBe('object'); + + expect(() => { + const res = vcrypto.verify(publicKey, data, sig); + expect(res).toBe(true); + }).not.toThrow(); }); - }); + } + }); diff --git a/veilid-wasm/wasm_build.sh b/veilid-wasm/wasm_build_dart.sh similarity index 91% rename from veilid-wasm/wasm_build.sh rename to veilid-wasm/wasm_build_dart.sh index bbc81291..63e45203 100755 --- a/veilid-wasm/wasm_build.sh +++ b/veilid-wasm/wasm_build_dart.sh @@ -14,7 +14,7 @@ if [[ "$1" == "release" ]]; then OUTPUTDIR=$SCRIPTDIR/../target/wasm32-unknown-unknown/release/pkg INPUTDIR=$SCRIPTDIR/../target/wasm32-unknown-unknown/release - ./wasm_remap_paths.sh cargo build --target wasm32-unknown-unknown --release + ./wasm_remap_paths.sh cargo build --target wasm32-unknown-unknown --release --no-default-features --features=default-dart mkdir -p $OUTPUTDIR wasm-bindgen --out-dir $OUTPUTDIR --target web --weak-refs $INPUTDIR/veilid_wasm.wasm wasm-opt -O --enable-mutable-globals $OUTPUTDIR/veilid_wasm_bg.wasm -o $OUTPUTDIR/veilid_wasm_bg.wasm.optimized @@ -23,7 +23,7 @@ else OUTPUTDIR=$SCRIPTDIR/../target/wasm32-unknown-unknown/debug/pkg INPUTDIR=$SCRIPTDIR/../target/wasm32-unknown-unknown/debug - RUSTFLAGS="-O -g $RUSTFLAGS" cargo build --target wasm32-unknown-unknown + RUSTFLAGS="-O -g $RUSTFLAGS" cargo build --target wasm32-unknown-unknown --no-default-features --features=default-dart mkdir -p $OUTPUTDIR wasm-bindgen --out-dir $OUTPUTDIR --target web --weak-refs --keep-debug --debug $INPUTDIR/veilid_wasm.wasm fi diff --git a/veilid-wasm/wasm_test.sh b/veilid-wasm/wasm_test_js.sh similarity index 100% rename from veilid-wasm/wasm_test.sh rename to veilid-wasm/wasm_test_js.sh