Merge branch 'dev' into 'main'

Merge Dev Multi-Crypto Support

See merge request veilid/veilid!17
This commit is contained in:
John Smith 2023-03-12 18:04:12 +00:00
commit 201bebc868
129 changed files with 9150 additions and 5824 deletions

34
.vscode/launch.json vendored
View File

@ -3,27 +3,44 @@
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"inputs": [
{
"id": "pickPid",
"type": "promptString",
"description": "Enter process id"
}
],
"configurations": [
{
"type": "lldb",
"request": "attach",
"name": "Attach to veilid-server",
"program": "${workspaceFolder}/target/debug/veilid-server",
"pid": "${command:pickMyProcess}"
"pid": "${command:pickMyProcess}",
"sourceLanguages": [
"rust"
]
},
{
"type": "lldb",
"request": "attach",
"name": "Attach to veilid-cli",
"program": "${workspaceFolder}/target/debug/veilid-cli",
"pid": "${command:pickMyProcess}"
"pid": "${command:pickMyProcess}",
"sourceLanguages": [
"rust"
]
},
{
"type": "lldb",
"request": "attach",
"name": "Attach to veilid-flutter example",
"program": "${workspaceFolder}/veilid-flutter/example/build/linux/x64/debug/bundle/veilid_example",
"pid": "${command:pickMyProcess}"
"pid": "${command:pickMyProcess}",
"sourceLanguages": [
"rust",
"dart"
]
},
{
"type": "lldb",
@ -42,16 +59,6 @@
],
"terminal": "console"
},
// {
// "type": "lldb",
// "request": "launch",
// "name": "Debug veilid-server",
// "cargo": {
// "args": ["run", "--manifest-path", "veilid-server/Cargo.toml"]
// },
// "args": ["--trace"],
// "cwd": "${workspaceFolder}/veilid-server"
// }
{
"type": "lldb",
"request": "launch",
@ -75,6 +82,7 @@
"args": [
"test",
"--no-run",
"--features=rt-tokio",
"--manifest-path",
"veilid-core/Cargo.toml"
],

397
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,8 @@ deps-android:
RUN mkdir /Android; mkdir /Android/Sdk
RUN curl -o /Android/cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip
RUN cd /Android; unzip /Android/cmdline-tools.zip
RUN yes | /Android/cmdline-tools/bin/sdkmanager --sdk_root=/Android/Sdk build-tools\;33.0.1 ndk\;25.1.8937393 cmake\;3.22.1 platform-tools platforms\;android-33
RUN yes | /Android/cmdline-tools/bin/sdkmanager --sdk_root=/Android/Sdk build-tools\;33.0.1 ndk\;25.1.8937393 cmake\;3.22.1 platform-tools platforms\;android-33 cmdline-tools\;latest
RUN rm -rf /Android/cmdline-tools
RUN apt-get clean
# Just linux build not android

View File

@ -35,6 +35,13 @@ command line without it. If you do so, you may skip to
#### Setup Dependencies using the CLI
You can automatically install the prerequisites using this script:
```shell
./install_linux_prerequisites.sh
```
Otherwise, you may choose to use Android `sdkmanager`. Follow the installation
instructions for `sdkmanager`
[here](https://developer.android.com/studio/command-line/sdkmanager), then use

View File

@ -50,7 +50,6 @@ core:
node_id: ''
node_id_secret: ''
bootstrap: ['bootstrap.dev.veilid.net']
bootstrap_nodes: []
routing_table:
limit_over_attached: 64
limit_fully_attached: 32

View File

@ -14,14 +14,14 @@ and the `veilid-server.conf` file.
## Global Directives
| Directive | Description |
|-------------------------------|-----------------------------------------|
| [daemon](#daemon) | Run `veilid-server` in the background |
| [client\_api](#client_api) ||
| [auto\_attach](#auto_attach) ||
| [logging](#logging) ||
| [testing](#testing) ||
| [core](#core) ||
| Directive | Description |
| ---------------------------- | ------------------------------------- |
| [daemon](#daemon) | Run `veilid-server` in the background |
| [client\_api](#client_api) | |
| [auto\_attach](#auto_attach) | |
| [logging](#logging) | |
| [testing](#testing) | |
| [core](#core) | |
### daemon
@ -39,10 +39,10 @@ client_api:
listen_address: 'localhost:5959'
```
| Parameter | Description |
|-----------------------------------------------|-------------|
| [enabled](#client_apienabled) ||
| [listen\_address](#client_apilisten_address) ||
| Parameter | Description |
| -------------------------------------------- | ----------- |
| [enabled](#client_apienabled) | |
| [listen\_address](#client_apilisten_address) | |
#### client\_api:enabled
@ -82,13 +82,13 @@ logging:
grpc_endpoint: 'localhost:4317'
```
| Parameter | Description |
|-------------------------------|-------------|
| [system](#loggingsystem) ||
| [terminal](#loggingterminal) ||
| [file](#loggingfile) ||
| [api](#loggingapi) ||
| [otlp](#loggingotlp) ||
| Parameter | Description |
| ---------------------------- | ----------- |
| [system](#loggingsystem) | |
| [terminal](#loggingterminal) | |
| [file](#loggingfile) | |
| [api](#loggingapi) | |
| [otlp](#loggingotlp) | |
#### logging:system
@ -142,12 +142,12 @@ testing:
### core
| Parameter | Description |
|-------------------------------------------|-------------|
| [protected\_store](#coreprotected_store) ||
| [table\_store](#coretable_store) ||
| [block\_store](#block_store) ||
| [network](#corenetwork) ||
| Parameter | Description |
| ---------------------------------------- | ----------- |
| [protected\_store](#coreprotected_store) | |
| [table\_store](#coretable_store) | |
| [block\_store](#block_store) | |
| [network](#corenetwork) | |
#### core:protected\_store
@ -191,7 +191,6 @@ network:
node_id: ''
node_id_secret: ''
bootstrap: ['bootstrap.dev.veilid.net']
bootstrap_nodes: []
upnp: true
detect_address_changes: true
enable_local_peer_scope: false
@ -199,13 +198,13 @@ network:
```
| Parameter | Description |
|---------------------------------------------|-------------|
| [routing\_table](#corenetworkrouting_table) ||
| [rpc](#corenetworkrpc) ||
| [dht](#corenetworkdht) ||
| [tls](#corenetworktls) ||
| [application](#corenetworkapplication) ||
| [protocol](#corenetworkprotocol) ||
| ------------------------------------------- | ----------- |
| [routing\_table](#corenetworkrouting_table) | |
| [rpc](#corenetworkrpc) | |
| [dht](#corenetworkdht) | |
| [tls](#corenetworktls) | |
| [application](#corenetworkapplication) | |
| [protocol](#corenetworkprotocol) | |
#### core:network:routing\_table

2
external/hashlink vendored

@ -1 +1 @@
Subproject commit a089b448071ef36633947693b90023c67dc8485f
Subproject commit ddab4623e19a8b3e9e3ee48be999908eebc54301

33
install_linux_prerequisites.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
set -eo pipefail
if [ $(id -u) -eq 0 ]; then
echo "Don't run this as root"
exit
fi
# Install APT dependencies
sudo apt update -y
sudo apt install -y openjdk-11-jdk-headless iproute2 curl build-essential cmake libssl-dev openssl file git pkg-config libdbus-1-dev libdbus-glib-1-dev libgirepository1.0-dev libcairo2-dev checkinstall unzip llvm wabt checkinstall
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y -c clippy --profile default
source "$HOME/.cargo/env"
# Install Android SDK
mkdir $HOME/Android; mkdir $HOME/Android/Sdk
curl -o $HOME/Android/cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip
cd $HOME/Android; unzip $HOME/Android/cmdline-tools.zip
$HOME/Android/cmdline-tools/bin/sdkmanager --sdk_root=$HOME/Android/Sdk build-tools\;33.0.1 ndk\;25.1.8937393 cmake\;3.22.1 platform-tools platforms\;android-33 cmdline-tools\;latest emulator
cd $HOME
rm -rf $HOME/Android/cmdline-tools $HOME/Android/cmdline-tools.zip
# Add environment variables
cat >> $HOME/.profile <<END
source "\$HOME/.cargo/env"
export PATH=\$PATH:\$HOME/Android/Sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin:\$HOME/Android/Sdk/platform-tools:\$HOME/Android/Sdk/cmdline-tools/latest/bin
export ANDROID_NDK_HOME=\$HOME/Android/Sdk/ndk/25.1.8937393
export ANDROID_SDK_ROOT=\$HOME/Android/Sdk
END
echo Exit and reopen the shell and continue with ./setup_linux.sh

View File

@ -91,12 +91,6 @@ rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-andro
# install cargo packages
cargo install wasm-bindgen-cli wasm-pack
# Ensure packages are installed
sudo apt-get install libc6-dev-i386 libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386 openjdk-11-jdk llvm wabt checkinstall
# Ensure android sdk packages are installed
$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager build-tools\;33.0.1 ndk\;25.1.8937393 cmake\;3.22.1 platform-tools platforms\;android-33
# Install capnproto using the same mechanism as our earthly build
$SCRIPTDIR/scripts/earthly/install_capnproto.sh
# Install protoc using the same mechanism as our earthly build

View File

@ -96,6 +96,9 @@ impl veilid_client::Server for VeilidClientImpl {
self.comproc.update_route(route);
}
VeilidUpdate::Shutdown => self.comproc.update_shutdown(),
VeilidUpdate::ValueChange(value_change) => {
self.comproc.update_value_change(value_change);
}
}
Promise::ok(())
@ -192,45 +195,55 @@ impl ClientApiConnection {
let rpc_jh = spawn_local(rpc_system);
// Send the request and get the state object and the registration object
let response = request
.send()
.promise
.await
.map_err(|e| format!("failed to send register request: {}", e))?;
let response = response
.get()
.map_err(|e| format!("failed to get register response: {}", e))?;
let reg_res: Result<registration::Client, String> = (async {
// Send the request and get the state object and the registration object
let response = request
.send()
.promise
.await
.map_err(|e| format!("failed to send register request: {}", e))?;
let response = response
.get()
.map_err(|e| format!("failed to get register response: {}", e))?;
// Get the registration object, which drops our connection when it is dropped
let _registration = response
.get_registration()
.map_err(|e| format!("failed to get registration object: {}", e))?;
// Get the registration object, which drops our connection when it is dropped
let registration = response
.get_registration()
.map_err(|e| format!("failed to get registration object: {}", e))?;
// Get the initial veilid state
let veilid_state = response
.get_state()
.map_err(|e| format!("failed to get initial veilid state: {}", e))?;
// Get the initial veilid state
let veilid_state = response
.get_state()
.map_err(|e| format!("failed to get initial veilid state: {}", e))?;
// Set up our state for the first time
let veilid_state: VeilidState = deserialize_json(veilid_state)
.map_err(|e| format!("failed to get deserialize veilid state: {}", e))?;
self.process_veilid_state(veilid_state).await?;
// Set up our state for the first time
let veilid_state: VeilidState = deserialize_json(veilid_state)
.map_err(|e| format!("failed to get deserialize veilid state: {}", e))?;
self.process_veilid_state(veilid_state).await?;
// Save server settings
let server_settings = response
.get_settings()
.map_err(|e| format!("failed to get initial veilid server settings: {}", e))?
.to_owned();
self.inner.borrow_mut().server_settings = Some(server_settings.clone());
// Save server settings
let server_settings = response
.get_settings()
.map_err(|e| format!("failed to get initial veilid server settings: {}", e))?
.to_owned();
self.inner.borrow_mut().server_settings = Some(server_settings.clone());
// Don't drop the registration, doing so will remove the client
// object mapping from the server which we need for the update backchannel
// Don't drop the registration, doing so will remove the client
// object mapping from the server which we need for the update backchannel
Ok(registration)
})
.await;
let _registration = match reg_res {
Ok(v) => v,
Err(e) => {
rpc_jh.abort().await;
return Err(e);
}
};
// Wait until rpc system completion or disconnect was requested
let res = rpc_jh.await;
// #[cfg(feature = "rt-tokio")]
// let res = res.map_err(|e| format!("join error: {}", e))?;
res.map_err(|e| format!("client RPC system error: {}", e))
}

View File

@ -424,6 +424,10 @@ reply - reply to an AppCall not handled directly by the server
self.inner().ui.add_node_event(out);
}
}
pub fn update_value_change(&mut self, value_change: veilid_core::VeilidValueChange) {
let out = format!("Value change: {:?}", value_change);
self.inner().ui.add_node_event(out);
}
pub fn update_log(&mut self, log: veilid_core::VeilidLog) {
self.inner().ui.add_node_event(format!(

View File

@ -50,7 +50,11 @@ fn format_bps(bps: ByteCount) -> String {
impl TableViewItem<PeerTableColumn> for PeerTableData {
fn to_column(&self, column: PeerTableColumn) -> String {
match column {
PeerTableColumn::NodeId => self.node_id.encode(),
PeerTableColumn::NodeId => self
.node_ids
.best()
.map(|n| n.value.encode())
.unwrap_or_else(|| "???".to_owned()),
PeerTableColumn::Address => format!(
"{:?}:{}",
self.peer_address.protocol_type(),
@ -74,7 +78,21 @@ impl TableViewItem<PeerTableColumn> for PeerTableData {
Self: Sized,
{
match column {
PeerTableColumn::NodeId => self.node_id.cmp(&other.node_id),
PeerTableColumn::NodeId => {
let n1 = self
.node_ids
.best()
.map(|n| n.value.encode())
.unwrap_or_else(|| "???".to_owned());
let n2 = other
.node_ids
.best()
.map(|n| n.value.encode())
.unwrap_or_else(|| "???".to_owned());
n1.cmp(&n2)
}
PeerTableColumn::Address => self.to_column(column).cmp(&other.to_column(column)),
PeerTableColumn::LatencyAvg => self
.peer_stats

View File

@ -881,13 +881,11 @@ impl UI {
}
pub fn set_config(&mut self, config: VeilidConfigInner) {
let mut inner = self.inner.borrow_mut();
inner.ui_state.node_id.set(
config
.network
.node_id
.map(|x| x.encode())
.unwrap_or("<unknown>".to_owned()),
);
inner
.ui_state
.node_id
.set(config.network.routing_table.node_id.to_string());
}
pub fn set_connection_state(&mut self, state: ConnectionState) {
let mut inner = self.inner.borrow_mut();

View File

@ -10,12 +10,14 @@ license = "LGPL-2.0-or-later OR MPL-2.0 OR (MIT AND BSD-3-Clause)"
crate-type = ["cdylib", "staticlib", "rlib"]
[features]
default = []
rt-async-std = [ "async-std", "async-std-resolver", "async_executors/async_std", "rtnetlink?/smol_socket", "veilid-tools/rt-async-std" ]
rt-tokio = [ "tokio", "tokio-util", "tokio-stream", "trust-dns-resolver/tokio-runtime", "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", "rtnetlink?/tokio_socket", "veilid-tools/rt-tokio" ]
default = [ "enable-crypto-vld0" ]
enable-crypto-vld0 = []
enable-crypto-none = []
rt-async-std = ["async-std", "async-std-resolver", "async_executors/async_std", "rtnetlink?/smol_socket", "veilid-tools/rt-async-std"]
rt-tokio = ["tokio", "tokio-util", "tokio-stream", "trust-dns-resolver/tokio-runtime", "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", "rtnetlink?/tokio_socket", "veilid-tools/rt-tokio"]
veilid_core_android_tests = [ "dep:paranoid-android" ]
veilid_core_ios_tests = [ "dep:tracing-oslog" ]
veilid_core_android_tests = ["dep:paranoid-android"]
veilid_core_ios_tests = ["dep:tracing-oslog"]
tracking = []
[dependencies]
@ -55,8 +57,8 @@ curve25519-dalek = { package = "curve25519-dalek-ng", version = "^4", default_fe
# ed25519-dalek needs rand 0.7 until it updates itself
rand = "0.7"
# curve25519-dalek-ng is stuck on digest 0.9.0
blake3 = { version = "1.1.0", default_features = false }
digest = "0.9.0"
blake3 = { version = "1.1.0" }
rtnetlink = { version = "^0", default-features = false, optional = true }
async-std-resolver = { version = "^0", optional = true }
trust-dns-resolver = { version = "^0", optional = true }
@ -65,6 +67,7 @@ keyvaluedb = { path = "../external/keyvaluedb/keyvaluedb" }
rkyv = { git = "https://github.com/rkyv/rkyv.git", rev = "57e2a8d", default_features = false, features = ["std", "alloc", "strict", "size_32", "validation"] }
bytecheck = "^0"
data-encoding = { version = "^2" }
weak-table = "0.3.2"
# Dependencies for native builds only
# Linux, Windows, Mac, iOS, Android

View File

@ -27,13 +27,24 @@ struct Nonce24 @0xb6260db25d8d7dfc {
u2 @2 :UInt64;
}
using NodeID = Key256;
using RoutePublicKey = Key256;
using ValueID = Key256;
using Nonce = Nonce24;
using Signature = Signature512;
using BlockID = Key256;
using TunnelID = UInt64;
using PublicKey = Key256; # Node id / DHT key / Route id, etc
using Nonce = Nonce24; # One-time encryption nonce
using Signature = Signature512; # Signature block
using TunnelID = UInt64; # Id for tunnels
using CryptoKind = UInt32; # FOURCC code for cryptography type
using ValueSeqNum = UInt32; # sequence numbers for values
using ValueSchema = UInt32; # FOURCC code for schema (0 = freeform, SUB0 = subkey control v0)
using Subkey = UInt32; # subkey index for dht
struct TypedKey @0xe2d567a9f1e61b29 {
kind @0 :CryptoKind;
key @1 :PublicKey;
}
struct TypedSignature @0x963170c7298e3884 {
kind @0 :CryptoKind;
signature @1 :Signature;
}
# Node Dial Info
################################################################
@ -123,7 +134,7 @@ struct RouteHopData @0x8ce231f9d1b7adf2 {
struct RouteHop @0xf8f672d75cce0c3b {
node :union {
nodeId @0 :NodeID; # node id only for established routes
nodeId @0 :PublicKey; # node id key only for established routes (kind is the same as the pr or sr it is part of)
peerInfo @1 :PeerInfo; # full peer info for this hop to establish the route
}
nextHop @2 :RouteHopData; # optional: If this the end of a private route, this field will not exist
@ -131,7 +142,7 @@ struct RouteHop @0xf8f672d75cce0c3b {
}
struct PrivateRoute @0x8a83fccb0851e776 {
publicKey @0 :RoutePublicKey; # private route public key (unique per private route)
publicKey @0 :TypedKey; # private route public key (unique per private route)
hopCount @1 :UInt8; # Count of hops left in the private route (for timeout calculation purposes only)
hops :union {
firstHop @2 :RouteHop; # first hop of a private route is unencrypted (hopcount > 0)
@ -141,7 +152,7 @@ struct PrivateRoute @0x8a83fccb0851e776 {
}
struct SafetyRoute @0xf554734d07cb5d59 {
publicKey @0 :RoutePublicKey; # safety route public key (unique per safety route)
publicKey @0 :TypedKey; # safety route public key (unique per safety route)
hopCount @1 :UInt8; # Count of hops left in the safety route (for timeout calculation purposes only)
hops :union {
data @2 :RouteHopData; # safety route has more hops
@ -149,21 +160,6 @@ struct SafetyRoute @0xf554734d07cb5d59 {
}
}
# Values
##############################
using ValueSeqNum = UInt32; # sequence numbers for values
struct ValueKey @0xe64b0992c21a0736 {
publicKey @0 :ValueID; # the location of the value
subkey @1 :Text; # the name of the subkey (or empty for the default subkey)
}
struct ValueData @0xb4b7416f169f2a3d {
seq @0 :ValueSeqNum; # sequence number of value
data @1 :Data; # value or subvalue contents
}
# Operations
##############################
@ -234,23 +230,23 @@ struct NodeInfo @0xe125d847e3f9f419 {
networkClass @0 :NetworkClass; # network class of this node
outboundProtocols @1 :ProtocolTypeSet; # protocols that can go outbound
addressTypes @2 :AddressTypeSet; # address types supported
minVersion @3 :UInt8; # minimum protocol version for rpc
maxVersion @4 :UInt8; # maximum protocol version for rpc
envelopeSupport @3 :List(UInt8); # supported rpc envelope/receipt versions
cryptoSupport @4 :List(CryptoKind); # cryptography systems supported
dialInfoDetailList @5 :List(DialInfoDetail); # inbound dial info details for this node
}
struct SignedDirectNodeInfo @0xe0e7ea3e893a3dd7 {
nodeInfo @0 :NodeInfo; # node info
timestamp @1 :UInt64; # when signed node info was generated
signature @2 :Signature; # signature
signatures @2 :List(TypedSignature); # signatures
}
struct SignedRelayedNodeInfo @0xb39e8428ccd87cbb {
nodeInfo @0 :NodeInfo; # node info
relayId @1 :NodeID; # node id for relay
relayIds @1 :List(TypedKey); # node ids for relay
relayInfo @2 :SignedDirectNodeInfo; # signed node info for relay
timestamp @3 :UInt64; # when signed node info was generated
signature @4 :Signature; # signature
signatures @4 :List(TypedSignature); # signatures
}
struct SignedNodeInfo @0xd2478ce5f593406a {
@ -261,16 +257,15 @@ struct SignedNodeInfo @0xd2478ce5f593406a {
}
struct PeerInfo @0xfe2d722d5d3c4bcb {
nodeId @0 :NodeID; # node id for 'closer peer'
nodeIds @0 :List(TypedKey); # node ids for 'closer peer'
signedNodeInfo @1 :SignedNodeInfo; # signed node info for 'closer peer'
}
struct RoutedOperation @0xcbcb8535b839e9dd {
version @0 :UInt8; # crypto version in use for the data
sequencing @1 :Sequencing; # sequencing preference to use to pass the message along
signatures @2 :List(Signature); # signatures from nodes that have handled the private route
nonce @3 :Nonce; # nonce Xmsg
data @4 :Data; # operation encrypted with ENC(Xmsg,DH(PKapr,SKbsr))
sequencing @0 :Sequencing; # sequencing preference to use to pass the message along
signatures @1 :List(Signature); # signatures from nodes that have handled the private route
nonce @2 :Nonce; # nonce Xmsg
data @3 :Data; # operation encrypted with ENC(Xmsg,DH(PKapr,SKbsr))
}
struct OperationStatusQ @0x865d80cea70d884a {
@ -293,7 +288,7 @@ struct OperationReturnReceipt @0xeb0fb5b5a9160eeb {
}
struct OperationFindNodeQ @0xfdef788fe9623bcd {
nodeId @0 :NodeID; # node id to locate
nodeId @0 :TypedKey; # node id to locate
}
struct OperationFindNodeA @0xa84cf2fb40c77089 {
@ -301,24 +296,36 @@ struct OperationFindNodeA @0xa84cf2fb40c77089 {
}
struct OperationRoute @0x96741859ce6ac7dd {
safetyRoute @0 :SafetyRoute; # Where this should go
operation @1 :RoutedOperation; # The operation to be routed
safetyRoute @0 :SafetyRoute; # where this should go
operation @1 :RoutedOperation; # the operation to be routed
}
struct OperationAppCallQ @0xade67b9f09784507 {
message @0 :Data; # Opaque request to application
message @0 :Data; # opaque request to application
}
struct OperationAppCallA @0xf7c797ac85f214b8 {
message @0 :Data; # Opaque response from application
message @0 :Data; # opaque response from application
}
struct OperationAppMessage @0x9baf542d81b411f5 {
message @0 :Data; # Opaque message to application
message @0 :Data; # opaque message to application
}
struct SubkeyRange {
start @0 :Subkey; # the start of a subkey range
end @1 :Subkey; # the end of a subkey range
}
struct ValueData @0xb4b7416f169f2a3d {
seq @0 :ValueSeqNum; # sequence number of value
schema @1 :ValueSchema; # fourcc code of schema for value
data @2 :Data; # value or subvalue contents
}
struct OperationGetValueQ @0xf88a5b6da5eda5d0 {
key @0 :ValueKey; # key for value to get
key @0 :TypedKey; # the location of the value
subkey @1 :Subkey; # the index of the subkey (0 for the default subkey)
}
struct OperationGetValueA @0xd896bb46f2e0249f {
@ -329,8 +336,9 @@ struct OperationGetValueA @0xd896bb46f2e0249f {
}
struct OperationSetValueQ @0xbac06191ff8bdbc5 {
key @0 :ValueKey; # key for value to update
value @1 :ValueData; # value or subvalue contents (older or equal seq number gets dropped)
key @0 :TypedKey; # the location of the value
subkey @1 :Subkey; # the index of the subkey (0 for the default subkey)
value @2 :ValueData; # value or subvalue contents (older or equal seq number gets dropped)
}
struct OperationSetValueA @0x9378d0732dc95be2 {
@ -341,7 +349,10 @@ struct OperationSetValueA @0x9378d0732dc95be2 {
}
struct OperationWatchValueQ @0xf9a5a6c547b9b228 {
key @0 :ValueKey; # key for value to watch
key @0 :TypedKey; # key for value to watch
subkeys @1 :List(SubkeyRange); # subkey range to watch, if empty, watch everything
expiration @2 :UInt64; # requested timestamp when this watch will expire in usec since epoch (can be return less, 0 for max)
count @3 :UInt32; # requested number of changes to watch for (0 = cancel, 1 = single shot, 2+ = counter, UINT32_MAX = continuous)
}
struct OperationWatchValueA @0xa726cab7064ba893 {
@ -350,12 +361,14 @@ struct OperationWatchValueA @0xa726cab7064ba893 {
}
struct OperationValueChanged @0xd1c59ebdd8cc1bf6 {
key @0 :ValueKey; # key for value that changed
value @1 :ValueData; # value or subvalue contents with sequence number
key @0 :TypedKey; # key for value that changed
subkeys @1 :List(SubkeyRange); # subkey range that changed (up to 512 ranges at a time)
count @2 :UInt32; # remaining changes left (0 means watch has expired)
value @3 :ValueData; # first value that changed (the rest can be gotten with getvalue)
}
struct OperationSupplyBlockQ @0xadbf4c542d749971 {
blockId @0 :BlockID; # hash of the block we can supply
blockId @0 :TypedKey; # hash of the block we can supply
}
struct OperationSupplyBlockA @0xf003822e83b5c0d7 {
@ -366,7 +379,7 @@ struct OperationSupplyBlockA @0xf003822e83b5c0d7 {
}
struct OperationFindBlockQ @0xaf4353ff004c7156 {
blockId @0 :BlockID; # hash of the block to locate
blockId @0 :TypedKey; # hash of the block to locate
}
struct OperationFindBlockA @0xc51455bc4915465d {
@ -516,7 +529,7 @@ struct Answer @0xacacb8b6988c1058 {
struct Operation @0xbf2811c435403c3b {
opId @0 :UInt64; # Random RPC ID. Must be random to foil reply forgery attacks.
senderNodeInfo @1 :SignedNodeInfo; # (optional) SignedNodeInfo for the sender to be cached by the receiver.
senderPeerInfo @1 :PeerInfo; # (optional) PeerInfo for the sender to be cached by the receiver.
targetNodeInfoTs @2 :UInt64; # Timestamp the sender believes the target's node info to be at or zero if not sent
kind :union {
question @3 :Question;

View File

@ -67,12 +67,6 @@ impl ServicesContext {
}
self.protected_store = Some(protected_store.clone());
// Init node id from config now that protected store is set up
if let Err(e) = self.config.init_node_id(protected_store.clone()).await {
self.shutdown().await;
return Err(e).wrap_err("init node id failed");
}
// Set up tablestore
trace!("init table store");
let table_store = TableStore::new(self.config.clone());
@ -84,7 +78,11 @@ impl ServicesContext {
// Set up crypto
trace!("init crypto");
let crypto = Crypto::new(self.config.clone(), table_store.clone());
let crypto = Crypto::new(
self.config.clone(),
table_store.clone(),
protected_store.clone(),
);
if let Err(e) = crypto.init().await {
self.shutdown().await;
return Err(e);

View File

@ -0,0 +1,266 @@
use super::*;
use core::cmp::{Eq, Ord, PartialEq, PartialOrd};
use core::convert::{TryFrom, TryInto};
use core::fmt;
use core::hash::Hash;
use data_encoding::BASE64URL_NOPAD;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
//////////////////////////////////////////////////////////////////////
/// Length of a public key in bytes
#[allow(dead_code)]
pub const PUBLIC_KEY_LENGTH: usize = 32;
/// Length of a public key in bytes after encoding to base64url
#[allow(dead_code)]
pub const PUBLIC_KEY_LENGTH_ENCODED: usize = 43;
/// Length of a secret key in bytes
#[allow(dead_code)]
pub const SECRET_KEY_LENGTH: usize = 32;
/// Length of a secret key in bytes after encoding to base64url
#[allow(dead_code)]
pub const SECRET_KEY_LENGTH_ENCODED: usize = 43;
/// Length of a signature in bytes
#[allow(dead_code)]
pub const SIGNATURE_LENGTH: usize = 64;
/// Length of a signature in bytes after encoding to base64url
#[allow(dead_code)]
pub const SIGNATURE_LENGTH_ENCODED: usize = 86;
/// Length of a nonce in bytes
#[allow(dead_code)]
pub const NONCE_LENGTH: usize = 24;
/// Length of a nonce in bytes after encoding to base64url
#[allow(dead_code)]
pub const NONCE_LENGTH_ENCODED: usize = 32;
/// Length of a shared secret in bytes
#[allow(dead_code)]
pub const SHARED_SECRET_LENGTH: usize = 32;
/// Length of a shared secret in bytes after encoding to base64url
#[allow(dead_code)]
pub const SHARED_SECRET_LENGTH_ENCODED: usize = 43;
/// Length of a route id in bytes
#[allow(dead_code)]
pub const ROUTE_ID_LENGTH: usize = 32;
/// Length of a route id in bytes afer encoding to base64url
#[allow(dead_code)]
pub const ROUTE_ID_LENGTH_ENCODED: usize = 43;
//////////////////////////////////////////////////////////////////////
pub trait Encodable
where
Self: Sized,
{
fn encode(&self) -> String;
fn encoded_len() -> usize;
fn try_decode<S: AsRef<str>>(input: S) -> Result<Self, VeilidAPIError> {
let b = input.as_ref().as_bytes();
Self::try_decode_bytes(b)
}
fn try_decode_bytes(b: &[u8]) -> Result<Self, VeilidAPIError>;
}
//////////////////////////////////////////////////////////////////////
macro_rules! byte_array_type {
($name:ident, $size:expr, $encoded_size:expr) => {
#[derive(
Clone,
Copy,
Hash,
Eq,
PartialEq,
PartialOrd,
Ord,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes, Hash, Eq, PartialEq, PartialOrd, Ord))]
pub struct $name {
pub bytes: [u8; $size],
}
impl Default for $name {
fn default() -> Self {
Self {
bytes: [0u8; $size],
}
}
}
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = self.encode();
serde::Serialize::serialize(&s, serializer)
}
}
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
if s == "" {
return Ok($name::default());
}
$name::try_decode(s.as_str()).map_err(serde::de::Error::custom)
}
}
impl $name {
pub fn new(bytes: [u8; $size]) -> Self {
Self { bytes }
}
pub fn try_from_vec(v: Vec<u8>) -> Result<Self, VeilidAPIError> {
let vl = v.len();
Ok(Self {
bytes: v.try_into().map_err(|_| {
VeilidAPIError::generic(format!(
"Expected a Vec of length {} but it was {}",
$size, vl
))
})?,
})
}
pub fn bit(&self, index: usize) -> bool {
assert!(index < ($size * 8));
let bi = index / 8;
let ti = 7 - (index % 8);
((self.bytes[bi] >> ti) & 1) != 0
}
pub fn first_nonzero_bit(&self) -> Option<usize> {
for i in 0..$size {
let b = self.bytes[i];
if b != 0 {
for n in 0..8 {
if ((b >> (7 - n)) & 1u8) != 0u8 {
return Some((i * 8) + n);
}
}
panic!("wtf")
}
}
None
}
pub fn nibble(&self, index: usize) -> u8 {
assert!(index < ($size * 2));
let bi = index / 2;
if index & 1 == 0 {
(self.bytes[bi] >> 4) & 0xFu8
} else {
self.bytes[bi] & 0xFu8
}
}
pub fn first_nonzero_nibble(&self) -> Option<(usize, u8)> {
for i in 0..($size * 2) {
let n = self.nibble(i);
if n != 0 {
return Some((i, n));
}
}
None
}
}
impl Encodable for $name {
fn encode(&self) -> String {
BASE64URL_NOPAD.encode(&self.bytes)
}
fn encoded_len() -> usize {
$encoded_size
}
fn try_decode_bytes(b: &[u8]) -> Result<Self, VeilidAPIError> {
let mut bytes = [0u8; $size];
let res = BASE64URL_NOPAD.decode_len(b.len());
match res {
Ok(v) => {
if v != $size {
apibail_generic!("Incorrect length in decode");
}
}
Err(_) => {
apibail_generic!("Failed to decode");
}
}
let res = BASE64URL_NOPAD.decode_mut(b, &mut bytes);
match res {
Ok(_) => Ok(Self::new(bytes)),
Err(_) => apibail_generic!("Failed to decode"),
}
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.encode())
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($name), "("))?;
write!(f, "{}", self.encode())?;
write!(f, ")")
}
}
impl From<&$name> for String {
fn from(value: &$name) -> Self {
value.encode()
}
}
impl FromStr for $name {
type Err = VeilidAPIError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
$name::try_from(s)
}
}
impl TryFrom<String> for $name {
type Error = VeilidAPIError;
fn try_from(value: String) -> Result<Self, Self::Error> {
$name::try_from(value.as_str())
}
}
impl TryFrom<&str> for $name {
type Error = VeilidAPIError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::try_decode(value)
}
}
};
}
/////////////////////////////////////////
byte_array_type!(PublicKey, PUBLIC_KEY_LENGTH, PUBLIC_KEY_LENGTH_ENCODED);
byte_array_type!(SecretKey, SECRET_KEY_LENGTH, SECRET_KEY_LENGTH_ENCODED);
byte_array_type!(Signature, SIGNATURE_LENGTH, SIGNATURE_LENGTH_ENCODED);
byte_array_type!(
PublicKeyDistance,
PUBLIC_KEY_LENGTH,
PUBLIC_KEY_LENGTH_ENCODED
);
byte_array_type!(Nonce, NONCE_LENGTH, NONCE_LENGTH_ENCODED);
byte_array_type!(
SharedSecret,
SHARED_SECRET_LENGTH,
SHARED_SECRET_LENGTH_ENCODED
);
byte_array_type!(RouteId, ROUTE_ID_LENGTH, ROUTE_ID_LENGTH_ENCODED);

View File

@ -0,0 +1,113 @@
use super::*;
pub trait CryptoSystem {
// Accessors
fn kind(&self) -> CryptoKind;
fn crypto(&self) -> Crypto;
// Cached Operations
fn cached_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError>;
// Generation
fn random_nonce(&self) -> Nonce;
fn random_shared_secret(&self) -> SharedSecret;
fn compute_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError>;
fn generate_keypair(&self) -> KeyPair;
fn generate_hash(&self, data: &[u8]) -> PublicKey;
fn generate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
) -> Result<PublicKey, VeilidAPIError>;
// Validation
fn validate_keypair(&self, dht_key: &PublicKey, dht_key_secret: &SecretKey) -> bool;
fn validate_hash(&self, data: &[u8], dht_key: &PublicKey) -> bool;
fn validate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
key: &PublicKey,
) -> Result<bool, VeilidAPIError>;
// Distance Metric
fn distance(&self, key1: &PublicKey, key2: &PublicKey) -> PublicKeyDistance;
// Authentication
fn sign(
&self,
key: &PublicKey,
secret: &SecretKey,
data: &[u8],
) -> Result<Signature, VeilidAPIError>;
fn verify(
&self,
key: &PublicKey,
data: &[u8],
signature: &Signature,
) -> Result<(), VeilidAPIError>;
// AEAD Encrypt/Decrypt
fn aead_overhead(&self) -> usize;
fn decrypt_in_place_aead(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError>;
fn decrypt_aead(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError>;
fn encrypt_in_place_aead(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError>;
fn encrypt_aead(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError>;
// NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
);
fn crypt_b2b_no_auth(
&self,
in_buf: &[u8],
out_buf: &mut [u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
);
fn crypt_no_auth_aligned_8(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) -> Vec<u8>;
fn crypt_no_auth_unaligned(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) -> Vec<u8>;
}

View File

@ -0,0 +1,45 @@
use super::*;
use crate::*;
// Diffie-Hellman key exchange cache
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct DHCacheKey {
pub key: PublicKey,
pub secret: SecretKey,
}
#[derive(Serialize, Deserialize)]
pub struct DHCacheValue {
pub shared_secret: SharedSecret,
}
pub type DHCache = LruCache<DHCacheKey, DHCacheValue>;
pub const DH_CACHE_SIZE: usize = 4096;
pub fn cache_to_bytes(cache: &DHCache) -> Vec<u8> {
let cnt: usize = cache.len();
let mut out: Vec<u8> = Vec::with_capacity(cnt * (32 + 32 + 32));
for e in cache.iter() {
out.extend(&e.0.key.bytes);
out.extend(&e.0.secret.bytes);
out.extend(&e.1.shared_secret.bytes);
}
let mut rev: Vec<u8> = Vec::with_capacity(out.len());
for d in out.chunks(32 + 32 + 32).rev() {
rev.extend(d);
}
rev
}
pub fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) {
for d in bytes.chunks(32 + 32 + 32) {
let k = DHCacheKey {
key: PublicKey::new(d[0..32].try_into().expect("asdf")),
secret: SecretKey::new(d[32..64].try_into().expect("asdf")),
};
let v = DHCacheValue {
shared_secret: SharedSecret::new(d[64..96].try_into().expect("asdf")),
};
cache.insert(k, v, |_k, _v| {});
}
}

View File

@ -1,69 +1,64 @@
#![allow(dead_code)]
#![allow(clippy::absurd_extreme_comparisons)]
use super::*;
use crate::routing_table::VersionRange;
use crate::*;
use core::convert::TryInto;
// #[repr(C, packed)]
// struct EnvelopeHeader {
// // Size is at least 8 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
// magic: [u8; 4], // 0x00: 0x56 0x4C 0x49 0x44 ("VLID")
// version: u8, // 0x04: 0 = EnvelopeV0
// min_version: u8, // 0x05: 0 = EnvelopeV0
// max_version: u8, // 0x06: 0 = EnvelopeV0
// reserved: u8, // 0x07: Reserved for future use
// }
// #[repr(C, packed)]
// struct EnvelopeV0 {
// // Size is 106 bytes.
// magic: [u8; 4], // 0x00: 0x56 0x4C 0x49 0x44 ("VLID")
// version: u8, // 0x04: 0 = EnvelopeV0
// min_version: u8, // 0x05: 0 = EnvelopeV0
// max_version: u8, // 0x06: 0 = EnvelopeV0
// reserved: u8, // 0x07: Reserved for future use
// 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 x25519
// sender_id: [u8; 32], // 0x2A: Node ID of the message source, which is the Ed25519 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 Ed25519 public key of the recipient (must be the receiving node, or a relay lease holder)
// // 0x6A: message is appended (operations)
// // encrypted by XChaCha20Poly1305(nonce,x25519(recipient_id, sender_secret_key))
// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature 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.
// }
/// 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
/// magic: [u8; 3], // 0x00: 0x56 0x4C 0x44 ("VLD")
/// version: u8, // 0x03: 0 = EnvelopeV0
/// }
///
/// #[repr(C, packed)]
/// struct EnvelopeV0 {
/// // Size is 106 bytes without signature and 170 with signature
/// magic: [u8; 3], // 0x00: 0x56 0x4C 0x44 ("VLD")
/// version: u8, // 0x03: 0 = EnvelopeV0
/// 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): Signature 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.
/// }
pub const MAX_ENVELOPE_SIZE: usize = 65507;
pub const MIN_ENVELOPE_SIZE: usize = 0x6A + 0x40; // Header + Signature
pub const ENVELOPE_MAGIC: &[u8; 4] = b"VLID";
pub type EnvelopeNonce = [u8; 24];
pub const ENVELOPE_MAGIC: &[u8; 3] = b"VLD";
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Envelope {
version: u8,
min_version: u8,
max_version: u8,
version: EnvelopeVersion,
crypto_kind: CryptoKind,
timestamp: Timestamp,
nonce: EnvelopeNonce,
sender_id: DHTKey,
recipient_id: DHTKey,
nonce: Nonce,
sender_id: PublicKey,
recipient_id: PublicKey,
}
impl Envelope {
pub fn new(
version: u8,
version: EnvelopeVersion,
crypto_kind: CryptoKind,
timestamp: Timestamp,
nonce: EnvelopeNonce,
sender_id: DHTKey,
recipient_id: DHTKey,
nonce: Nonce,
sender_id: PublicKey,
recipient_id: PublicKey,
) -> Self {
assert!(version >= MIN_CRYPTO_VERSION);
assert!(version <= MAX_CRYPTO_VERSION);
assert!(VALID_ENVELOPE_VERSIONS.contains(&version));
assert!(VALID_CRYPTO_KINDS.contains(&crypto_kind));
Self {
version,
min_version: MIN_CRYPTO_VERSION,
max_version: MAX_CRYPTO_VERSION,
crypto_kind,
timestamp,
nonce,
sender_id,
@ -71,7 +66,7 @@ impl Envelope {
}
}
pub fn from_signed_data(data: &[u8]) -> Result<Envelope, VeilidAPIError> {
pub fn from_signed_data(crypto: Crypto, data: &[u8]) -> Result<Envelope, VeilidAPIError> {
// 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 {
@ -79,33 +74,28 @@ impl Envelope {
}
// Verify magic number
let magic: [u8; 4] = data[0x00..0x04]
let magic: [u8; 3] = data[0x00..0x03]
.try_into()
.map_err(VeilidAPIError::internal)?;
if magic != *ENVELOPE_MAGIC {
apibail_generic!("bad magic number");
}
// Check version
let version = data[0x04];
if version > MAX_CRYPTO_VERSION || version < MIN_CRYPTO_VERSION {
apibail_parse_error!("unsupported cryptography version", version);
// Check envelope version
let version = data[0x03];
if !VALID_ENVELOPE_VERSIONS.contains(&version) {
apibail_parse_error!("unsupported envelope version", version);
}
// Get min version
let min_version = data[0x05];
if min_version > version {
apibail_parse_error!("version too low", version);
}
// Get max version
let max_version = data[0x06];
if version > max_version {
apibail_parse_error!("version too high", version);
}
if min_version > max_version {
apibail_generic!("version information invalid");
}
// Check crypto kind
let crypto_kind = FourCC(
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(
@ -136,17 +126,18 @@ impl Envelope {
.into();
// Get nonce and sender node id
let nonce: EnvelopeNonce = data[0x12..0x2A]
let nonce_slice: [u8; NONCE_LENGTH] = data[0x12..0x2A]
.try_into()
.map_err(VeilidAPIError::internal)?;
let sender_id_slice: [u8; 32] = data[0x2A..0x4A]
let sender_id_slice: [u8; PUBLIC_KEY_LENGTH] = data[0x2A..0x4A]
.try_into()
.map_err(VeilidAPIError::internal)?;
let recipient_id_slice: [u8; 32] = data[0x4A..0x6A]
let recipient_id_slice: [u8; PUBLIC_KEY_LENGTH] = data[0x4A..0x6A]
.try_into()
.map_err(VeilidAPIError::internal)?;
let sender_id = DHTKey::new(sender_id_slice);
let recipient_id = DHTKey::new(recipient_id_slice);
let nonce: Nonce = Nonce::new(nonce_slice);
let sender_id = PublicKey::new(sender_id_slice);
let recipient_id = PublicKey::new(recipient_id_slice);
// Ensure sender_id and recipient_id are not the same
if sender_id == recipient_id {
@ -157,21 +148,21 @@ impl Envelope {
}
// Get signature
let signature = DHTSignature::new(
let signature = Signature::new(
data[(data.len() - 64)..]
.try_into()
.map_err(VeilidAPIError::internal)?,
);
// Validate signature
verify(&sender_id, &data[0..(data.len() - 64)], &signature)
vcrypto
.verify(&sender_id, &data[0..(data.len() - 64)], &signature)
.map_err(VeilidAPIError::internal)?;
// Return envelope
Ok(Self {
version,
min_version,
max_version,
crypto_kind,
timestamp,
nonce,
sender_id,
@ -183,13 +174,17 @@ impl Envelope {
&self,
crypto: Crypto,
data: &[u8],
node_id_secret: &DHTKeySecret,
node_id_secret: &SecretKey,
) -> Result<Vec<u8>, VeilidAPIError> {
// Get DH secret
let dh_secret = crypto.cached_dh(&self.sender_id, node_id_secret)?;
let vcrypto = crypto
.get(self.crypto_kind)
.expect("need to ensure only valid crypto kinds here");
let dh_secret = vcrypto.cached_dh(&self.sender_id, node_id_secret)?;
// Decrypt message without authentication
let body = Crypto::crypt_no_auth(&data[0x6A..data.len() - 64], &self.nonce, &dh_secret);
let body =
vcrypto.crypt_no_auth_aligned_8(&data[0x6A..data.len() - 64], &self.nonce, &dh_secret);
Ok(body)
}
@ -198,39 +193,41 @@ impl Envelope {
&self,
crypto: Crypto,
body: &[u8],
node_id_secret: &DHTKeySecret,
node_id_secret: &SecretKey,
) -> Result<Vec<u8>, VeilidAPIError> {
// 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 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 dh_secret = vcrypto.cached_dh(&self.recipient_id, node_id_secret)?;
// Write envelope body
let mut data = vec![0u8; envelope_size];
// Write magic
data[0x00..0x04].copy_from_slice(ENVELOPE_MAGIC);
data[0x00..0x03].copy_from_slice(ENVELOPE_MAGIC);
// Write version
data[0x04] = self.version;
// Write min version
data[0x05] = self.min_version;
// Write max version
data[0x06] = self.max_version;
data[0x03] = self.version;
// 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);
data[0x12..0x2A].copy_from_slice(&self.nonce.bytes);
// Write sender node id
data[0x2A..0x4A].copy_from_slice(&self.sender_id.bytes);
// Write recipient node id
data[0x4A..0x6A].copy_from_slice(&self.recipient_id.bytes);
// Generate dh secret
let dh_secret = crypto.cached_dh(&self.recipient_id, node_id_secret)?;
// Encrypt and authenticate message
let encrypted_body = Crypto::crypt_no_auth(body, &self.nonce, &dh_secret);
let encrypted_body = vcrypto.crypt_no_auth_unaligned(body, &self.nonce, &dh_secret);
// Write body
if !encrypted_body.is_empty() {
@ -238,7 +235,7 @@ impl Envelope {
}
// Sign the envelope
let signature = sign(
let signature = vcrypto.sign(
&self.sender_id,
node_id_secret,
&data[0..(envelope_size - 64)],
@ -254,25 +251,23 @@ impl Envelope {
self.version
}
pub fn get_min_max_version(&self) -> VersionRange {
VersionRange {
min: self.min_version,
max: self.max_version,
}
pub fn get_crypto_kind(&self) -> CryptoKind {
self.crypto_kind
}
pub fn get_timestamp(&self) -> Timestamp {
self.timestamp
}
pub fn get_nonce(&self) -> EnvelopeNonce {
pub fn get_nonce(&self) -> Nonce {
self.nonce
}
pub fn get_sender_id(&self) -> DHTKey {
pub fn get_sender_id(&self) -> PublicKey {
self.sender_id
}
pub fn get_recipient_id(&self) -> DHTKey {
pub fn get_recipient_id(&self) -> PublicKey {
self.recipient_id
}
}

View File

@ -1,389 +0,0 @@
use crate::*;
use core::cmp::{Eq, Ord, PartialEq, PartialOrd};
use core::convert::{TryFrom, TryInto};
use core::fmt;
use core::hash::Hash;
use data_encoding::BASE64URL_NOPAD;
use digest::generic_array::typenum::U64;
use digest::{Digest, Output};
use ed25519_dalek::{Keypair, PublicKey, Signature};
use generic_array::GenericArray;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
//////////////////////////////////////////////////////////////////////
/// Length of a DHT key in bytes
#[allow(dead_code)]
pub const DHT_KEY_LENGTH: usize = 32;
/// Length of a DHT key in bytes after encoding to base64url
#[allow(dead_code)]
pub const DHT_KEY_LENGTH_ENCODED: usize = 43;
/// Length of a DHT secret in bytes
#[allow(dead_code)]
pub const DHT_KEY_SECRET_LENGTH: usize = 32;
/// Length of a DHT secret in bytes after encoding to base64url
#[allow(dead_code)]
pub const DHT_KEY_SECRET_LENGTH_ENCODED: usize = 43;
/// Length of a DHT signature in bytes
#[allow(dead_code)]
/// Length of a DHT signature in bytes after encoding to base64url
pub const DHT_SIGNATURE_LENGTH: usize = 64;
#[allow(dead_code)]
pub const DHT_SIGNATURE_LENGTH_ENCODED: usize = 86;
//////////////////////////////////////////////////////////////////////
macro_rules! byte_array_type {
($name:ident, $size:expr) => {
#[derive(
Clone,
Copy,
Hash,
Eq,
PartialEq,
PartialOrd,
Ord,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes, Hash, Eq, PartialEq, PartialOrd, Ord))]
pub struct $name {
pub bytes: [u8; $size],
}
impl Default for $name {
fn default() -> Self {
Self {
bytes: [0u8; $size],
}
}
}
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = self.encode();
serde::Serialize::serialize(&s, serializer)
}
}
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String as serde::Deserialize>::deserialize(deserializer)?;
if s == "" {
return Ok($name::default());
}
$name::try_decode(s.as_str()).map_err(serde::de::Error::custom)
}
}
impl $name {
pub fn new(bytes: [u8; $size]) -> Self {
Self { bytes }
}
pub fn try_from_vec(v: Vec<u8>) -> Result<Self, VeilidAPIError> {
let vl = v.len();
Ok(Self {
bytes: v.try_into().map_err(|_| {
VeilidAPIError::generic(format!(
"Expected a Vec of length {} but it was {}",
$size, vl
))
})?,
})
}
pub fn bit(&self, index: usize) -> bool {
assert!(index < ($size * 8));
let bi = index / 8;
let ti = 7 - (index % 8);
((self.bytes[bi] >> ti) & 1) != 0
}
pub fn first_nonzero_bit(&self) -> Option<usize> {
for i in 0..$size {
let b = self.bytes[i];
if b != 0 {
for n in 0..8 {
if ((b >> (7 - n)) & 1u8) != 0u8 {
return Some((i * 8) + n);
}
}
panic!("wtf")
}
}
None
}
pub fn nibble(&self, index: usize) -> u8 {
assert!(index < ($size * 2));
let bi = index / 2;
if index & 1 == 0 {
(self.bytes[bi] >> 4) & 0xFu8
} else {
self.bytes[bi] & 0xFu8
}
}
pub fn first_nonzero_nibble(&self) -> Option<(usize, u8)> {
for i in 0..($size * 2) {
let n = self.nibble(i);
if n != 0 {
return Some((i, n));
}
}
None
}
pub fn encode(&self) -> String {
BASE64URL_NOPAD.encode(&self.bytes)
}
pub fn try_decode<S: AsRef<str>>(input: S) -> Result<Self, VeilidAPIError> {
let mut bytes = [0u8; $size];
let res = BASE64URL_NOPAD.decode_len(input.as_ref().len());
match res {
Ok(v) => {
if v != $size {
apibail_generic!("Incorrect length in decode");
}
}
Err(_) => {
apibail_generic!("Failed to decode");
}
}
let res = BASE64URL_NOPAD.decode_mut(input.as_ref().as_bytes(), &mut bytes);
match res {
Ok(_) => Ok(Self::new(bytes)),
Err(_) => apibail_generic!("Failed to decode"),
}
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
//write!(f, "{}", String::from(self))
write!(f, "{}", self.encode())
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($name), "("))?;
write!(f, "{}", self.encode())?;
write!(f, ")")
}
}
impl From<&$name> for String {
fn from(value: &$name) -> Self {
// let mut s = String::new();
// for n in 0..($size / 8) {
// let b: [u8; 8] = value.bytes[n * 8..(n + 1) * 8].try_into().unwrap();
// s.push_str(hex::encode(b).as_str());
// }
// s
value.encode()
}
}
impl TryFrom<String> for $name {
type Error = VeilidAPIError;
fn try_from(value: String) -> Result<Self, Self::Error> {
$name::try_from(value.as_str())
}
}
impl TryFrom<&str> for $name {
type Error = VeilidAPIError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
// let mut out = $name::default();
// if value == "" {
// return Ok(out);
// }
// if value.len() != ($size * 2) {
// apibail_generic!(concat!(stringify!($name), " is incorrect length"));
// }
// match hex::decode_to_slice(value, &mut out.bytes) {
// Ok(_) => Ok(out),
// Err(err) => Err(VeilidAPIError::generic(err)),
// }
Self::try_decode(value)
}
}
};
}
byte_array_type!(DHTKey, DHT_KEY_LENGTH);
byte_array_type!(DHTKeySecret, DHT_KEY_SECRET_LENGTH);
byte_array_type!(DHTSignature, DHT_SIGNATURE_LENGTH);
byte_array_type!(DHTKeyDistance, DHT_KEY_LENGTH);
/////////////////////////////////////////
struct Blake3Digest512 {
dig: blake3::Hasher,
}
impl Digest for Blake3Digest512 {
type OutputSize = U64;
fn new() -> Self {
Self {
dig: blake3::Hasher::new(),
}
}
fn update(&mut self, data: impl AsRef<[u8]>) {
self.dig.update(data.as_ref());
}
fn chain(mut self, data: impl AsRef<[u8]>) -> Self
where
Self: Sized,
{
self.update(data);
self
}
fn finalize(self) -> Output<Self> {
let mut b = [0u8; 64];
self.dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
out
}
fn finalize_reset(&mut self) -> Output<Self> {
let mut b = [0u8; 64];
self.dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
self.reset();
out
}
fn reset(&mut self) {
self.dig.reset();
}
fn output_size() -> usize {
64
}
fn digest(data: &[u8]) -> Output<Self> {
let mut dig = blake3::Hasher::new();
dig.update(data);
let mut b = [0u8; 64];
dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
out
}
}
/////////////////////////////////////////
pub fn generate_secret() -> (DHTKey, DHTKeySecret) {
let mut csprng = VeilidRng {};
let keypair = Keypair::generate(&mut csprng);
let dht_key = DHTKey::new(keypair.public.to_bytes());
let dht_key_secret = DHTKeySecret::new(keypair.secret.to_bytes());
(dht_key, dht_key_secret)
}
pub fn sign(
dht_key: &DHTKey,
dht_key_secret: &DHTKeySecret,
data: &[u8],
) -> Result<DHTSignature, VeilidAPIError> {
let mut kpb: [u8; DHT_KEY_SECRET_LENGTH + DHT_KEY_LENGTH] =
[0u8; DHT_KEY_SECRET_LENGTH + DHT_KEY_LENGTH];
kpb[..DHT_KEY_SECRET_LENGTH].copy_from_slice(&dht_key_secret.bytes);
kpb[DHT_KEY_SECRET_LENGTH..].copy_from_slice(&dht_key.bytes);
let keypair = Keypair::from_bytes(&kpb)
.map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?;
let mut dig = Blake3Digest512::new();
dig.update(data);
let sig = keypair
.sign_prehashed(dig, None)
.map_err(VeilidAPIError::internal)?;
let dht_sig = DHTSignature::new(sig.to_bytes());
Ok(dht_sig)
}
pub fn verify(
dht_key: &DHTKey,
data: &[u8],
signature: &DHTSignature,
) -> Result<(), VeilidAPIError> {
let pk = PublicKey::from_bytes(&dht_key.bytes)
.map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?;
let sig = Signature::from_bytes(&signature.bytes)
.map_err(|e| VeilidAPIError::parse_error("Signature is invalid", e))?;
let mut dig = Blake3Digest512::new();
dig.update(data);
pk.verify_prehashed(dig, None, &sig)
.map_err(|e| VeilidAPIError::parse_error("Verification failed", e))?;
Ok(())
}
pub fn generate_hash(data: &[u8]) -> DHTKey {
DHTKey::new(*blake3::hash(data).as_bytes())
}
pub fn validate_hash(data: &[u8], dht_key: &DHTKey) -> bool {
let bytes = *blake3::hash(data).as_bytes();
bytes == dht_key.bytes
}
pub fn validate_key(dht_key: &DHTKey, dht_key_secret: &DHTKeySecret) -> bool {
let data = vec![0u8; 512];
let sig = match sign(dht_key, dht_key_secret, &data) {
Ok(s) => s,
Err(_) => {
return false;
}
};
verify(dht_key, &data, &sig).is_ok()
}
pub fn distance(key1: &DHTKey, key2: &DHTKey) -> DHTKeyDistance {
let mut bytes = [0u8; DHT_KEY_LENGTH];
for (n, byte) in bytes.iter_mut().enumerate() {
*byte = key1.bytes[n] ^ key2.bytes[n];
}
DHTKeyDistance::new(bytes)
}
#[allow(dead_code)]
pub fn sort_closest_fn(key: DHTKey) -> impl FnMut(&DHTKey, &DHTKey) -> std::cmp::Ordering {
move |k1, k2| distance(k1, &key).cmp(&distance(k2, &key))
}

View File

@ -1,126 +1,141 @@
mod byte_array_types;
mod dh_cache;
mod envelope;
mod key;
mod receipt;
mod types;
mod value;
pub mod crypto_system;
pub mod tests;
pub mod vld0;
pub use byte_array_types::*;
pub use crypto_system::*;
pub use dh_cache::*;
pub use envelope::*;
pub use key::*;
pub use receipt::*;
pub use types::*;
pub use value::*;
pub const MIN_CRYPTO_VERSION: u8 = 0u8;
pub const MAX_CRYPTO_VERSION: u8 = 0u8;
pub use vld0::*;
use crate::*;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use chacha20poly1305 as ch;
use chacha20poly1305::aead::{AeadInPlace, NewAead};
use core::convert::TryInto;
use curve25519_dalek as cd;
use ed25519_dalek as ed;
use hashlink::linked_hash_map::Entry;
use hashlink::LruCache;
use serde::{Deserialize, Serialize};
use x25519_dalek as xd;
// Handle to a particular cryptosystem
pub type CryptoSystemVersion = Arc<dyn CryptoSystem + Send + Sync>;
pub type SharedSecret = [u8; 32];
pub type Nonce = [u8; 24];
const DH_CACHE_SIZE: usize = 1024;
pub const AEAD_OVERHEAD: usize = 16;
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)]
struct DHCacheKey {
key: DHTKey,
secret: DHTKeySecret,
/// Crypto kinds in order of preference, best cryptosystem is the first one, worst is the last one
pub const VALID_CRYPTO_KINDS: [CryptoKind; 1] = [CRYPTO_KIND_VLD0];
/// 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 {
VALID_CRYPTO_KINDS[0]
}
#[derive(Serialize, Deserialize)]
struct DHCacheValue {
shared_secret: SharedSecret,
}
type DHCache = LruCache<DHCacheKey, DHCacheValue>;
// Version number of envelope format
pub type EnvelopeVersion = u8;
fn cache_to_bytes(cache: &DHCache) -> Vec<u8> {
let cnt: usize = cache.len();
let mut out: Vec<u8> = Vec::with_capacity(cnt * (32 + 32 + 32));
for e in cache.iter() {
out.extend(&e.0.key.bytes);
out.extend(&e.0.secret.bytes);
out.extend(&e.1.shared_secret);
}
let mut rev: Vec<u8> = Vec::with_capacity(out.len());
for d in out.chunks(32 + 32 + 32).rev() {
rev.extend(d);
}
rev
}
fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) {
for d in bytes.chunks(32 + 32 + 32) {
let k = DHCacheKey {
key: DHTKey::new(d[0..32].try_into().expect("asdf")),
secret: DHTKeySecret::new(d[32..64].try_into().expect("asdf")),
};
let v = DHCacheValue {
shared_secret: d[64..96].try_into().expect("asdf"),
};
cache.insert(k, v);
}
/// 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] = [0u8];
/// Number of envelope versions to keep on structures if many are present beyond the ones we consider valid
pub const MAX_ENVELOPE_VERSIONS: usize = 3;
/// Return the best envelope version we support
pub fn best_envelope_version() -> EnvelopeVersion {
VALID_ENVELOPE_VERSIONS[0]
}
struct CryptoInner {
table_store: TableStore,
node_id: DHTKey,
node_id_secret: DHTKeySecret,
dh_cache: DHCache,
flush_future: Option<SendPinBoxFuture<()>>,
crypto_vld0: Option<Arc<dyn CryptoSystem + Send + Sync>>,
}
struct CryptoUnlockedInner {
config: VeilidConfig,
table_store: TableStore,
protected_store: ProtectedStore,
}
/// Crypto factory implementation
#[derive(Clone)]
pub struct Crypto {
config: VeilidConfig,
unlocked_inner: Arc<CryptoUnlockedInner>,
inner: Arc<Mutex<CryptoInner>>,
}
impl Crypto {
fn new_inner(table_store: TableStore) -> CryptoInner {
fn new_inner() -> CryptoInner {
CryptoInner {
table_store,
node_id: Default::default(),
node_id_secret: Default::default(),
dh_cache: DHCache::new(DH_CACHE_SIZE),
flush_future: None,
crypto_vld0: None,
}
}
pub fn new(config: VeilidConfig, table_store: TableStore) -> Self {
Self {
config,
inner: Arc::new(Mutex::new(Self::new_inner(table_store))),
}
pub fn new(
config: VeilidConfig,
table_store: TableStore,
protected_store: ProtectedStore,
) -> Self {
let out = Self {
unlocked_inner: Arc::new(CryptoUnlockedInner {
config,
table_store,
protected_store,
}),
inner: Arc::new(Mutex::new(Self::new_inner())),
};
out.inner.lock().crypto_vld0 = Some(Arc::new(vld0::CryptoSystemVLD0::new(out.clone())));
out
}
pub fn config(&self) -> VeilidConfig {
self.unlocked_inner.config.clone()
}
pub async fn init(&self) -> EyreResult<()> {
trace!("Crypto::init");
let table_store = self.unlocked_inner.table_store.clone();
// Init node id from config
if let Err(e) = self
.unlocked_inner
.config
.init_node_ids(self.clone(), self.unlocked_inner.protected_store.clone())
.await
{
return Err(e).wrap_err("init node id failed");
}
// make local copy of node id for easy access
let (table_store, node_id) = {
let mut inner = self.inner.lock();
let c = self.config.get();
inner.node_id = c.network.node_id.unwrap();
inner.node_id_secret = c.network.node_id_secret.unwrap();
(inner.table_store.clone(), c.network.node_id)
let mut cache_validity_key: Vec<u8> = Vec::new();
{
let c = self.unlocked_inner.config.get();
for ck in VALID_CRYPTO_KINDS {
cache_validity_key.append(
&mut c
.network
.routing_table
.node_id
.get(ck)
.unwrap()
.value
.bytes
.to_vec(),
);
}
};
// load caches if they are valid for this node id
let mut db = table_store.open("crypto_caches", 1).await?;
let caches_valid = match db.load(0, b"node_id")? {
Some(v) => v.as_slice() == node_id.unwrap().bytes,
let caches_valid = match db.load(0, b"cache_validity_key")? {
Some(v) => v == cache_validity_key,
None => false,
};
if caches_valid {
@ -132,7 +147,8 @@ impl Crypto {
drop(db);
table_store.delete("crypto_caches").await?;
db = table_store.open("crypto_caches", 1).await?;
db.store(0, b"node_id", &node_id.unwrap().bytes).await?;
db.store(0, b"cache_validity_key", &cache_validity_key)
.await?;
}
// Schedule flushing
@ -152,13 +168,16 @@ impl Crypto {
pub async fn flush(&self) -> EyreResult<()> {
//trace!("Crypto::flush");
let (table_store, cache_bytes) = {
let cache_bytes = {
let inner = self.inner.lock();
let cache_bytes = cache_to_bytes(&inner.dh_cache);
(inner.table_store.clone(), cache_bytes)
cache_to_bytes(&inner.dh_cache)
};
let db = table_store.open("crypto_caches", 1).await?;
let db = self
.unlocked_inner
.table_store
.open("crypto_caches", 1)
.await?;
db.store(0, b"dh_cache", &cache_bytes).await?;
Ok(())
}
@ -180,139 +199,99 @@ impl Crypto {
};
}
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, VeilidAPIError> {
let bytes = key.to_bytes();
let compressed = cd::edwards::CompressedEdwardsY(bytes);
let point = compressed
.decompress()
.ok_or_else(|| VeilidAPIError::internal("ed25519_to_x25519_pk failed"))?;
let mp = point.to_montgomery();
Ok(xd::PublicKey::from(mp.to_bytes()))
}
fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result<xd::StaticSecret, VeilidAPIError> {
let exp = ed::ExpandedSecretKey::from(key);
let bytes: [u8; ed::EXPANDED_SECRET_KEY_LENGTH] = exp.to_bytes();
let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(VeilidAPIError::internal)?;
Ok(xd::StaticSecret::from(lowbytes))
/// Factory method to get a specific crypto version
pub fn get(&self, kind: CryptoKind) -> Option<CryptoSystemVersion> {
let inner = self.inner.lock();
match kind {
CRYPTO_KIND_VLD0 => Some(inner.crypto_vld0.clone().unwrap()),
_ => None,
}
}
pub fn cached_dh(
// Factory method to get the best crypto version
pub fn best(&self) -> CryptoSystemVersion {
self.get(best_crypto_kind()).unwrap()
}
/// Signature set verification
/// Returns the set of signature cryptokinds that validate and are supported
/// If any cryptokinds are supported and do not validate, the whole operation
/// returns an error
pub fn verify_signatures(
&self,
key: &DHTKey,
secret: &DHTKeySecret,
node_ids: &[TypedKey],
data: &[u8],
typed_signatures: &[TypedSignature],
) -> Result<TypedKeySet, VeilidAPIError> {
let mut out = TypedKeySet::with_capacity(node_ids.len());
for sig in typed_signatures {
for nid in node_ids {
if nid.kind == sig.kind {
if let Some(vcrypto) = self.get(sig.kind) {
vcrypto.verify(&nid.value, data, &sig.value)?;
out.add(*nid);
}
}
}
}
Ok(out)
}
/// Signature set generation
/// Generates the set of signatures that are supported
/// Any cryptokinds that are not supported are silently dropped
pub fn generate_signatures<F, R>(
&self,
data: &[u8],
typed_key_pairs: &[TypedKeyPair],
transform: F,
) -> Result<Vec<R>, VeilidAPIError>
where
F: Fn(&TypedKeyPair, Signature) -> R,
{
let mut out = Vec::<R>::with_capacity(typed_key_pairs.len());
for kp in typed_key_pairs {
if let Some(vcrypto) = self.get(kp.kind) {
let sig = vcrypto.sign(&kp.value.key, &kp.value.secret, data)?;
out.push(transform(kp, sig))
}
}
Ok(out)
}
/// Generate keypair
/// Does not require startup/init
pub fn generate_keypair(crypto_kind: CryptoKind) -> Result<TypedKeyPair, VeilidAPIError> {
if crypto_kind == CRYPTO_KIND_VLD0 {
let kp = vld0_generate_keypair();
return Ok(TypedKeyPair::new(crypto_kind, kp));
}
Err(VeilidAPIError::generic("invalid crypto kind"))
}
// Internal utilities
fn cached_dh_internal<T: CryptoSystem>(
&self,
vcrypto: &T,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
Ok(
match self.inner.lock().dh_cache.entry(DHCacheKey {
key: *key,
secret: *secret,
}) {
match self.inner.lock().dh_cache.entry(
DHCacheKey {
key: *key,
secret: *secret,
},
|_k, _v| {},
) {
Entry::Occupied(e) => e.get().shared_secret,
Entry::Vacant(e) => {
let shared_secret = Self::compute_dh(key, secret)?;
let shared_secret = vcrypto.compute_dh(key, secret)?;
e.insert(DHCacheValue { shared_secret });
shared_secret
}
},
)
}
///////////
// These are safe to use regardless of initialization status
pub fn compute_dh(key: &DHTKey, secret: &DHTKeySecret) -> Result<SharedSecret, VeilidAPIError> {
let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(VeilidAPIError::internal)?;
let pk_xd = Self::ed25519_to_x25519_pk(&pk_ed)?;
let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(VeilidAPIError::internal)?;
let sk_xd = Self::ed25519_to_x25519_sk(&sk_ed)?;
Ok(sk_xd.diffie_hellman(&pk_xd).to_bytes())
}
pub fn get_random_nonce() -> Nonce {
let mut nonce = [0u8; 24];
random_bytes(&mut nonce).unwrap();
nonce
}
pub fn get_random_secret() -> SharedSecret {
let mut s = [0u8; 32];
random_bytes(&mut s).unwrap();
s
}
pub fn decrypt_in_place_aead(
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
let key = ch::Key::from(*shared_secret);
let xnonce = ch::XNonce::from(*nonce);
let aead = ch::XChaCha20Poly1305::new(&key);
aead.decrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)
}
pub fn decrypt_aead(
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
let mut out = body.to_vec();
Self::decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)?;
Ok(out)
}
pub fn encrypt_in_place_aead(
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
let key = ch::Key::from(*shared_secret);
let xnonce = ch::XNonce::from(*nonce);
let aead = ch::XChaCha20Poly1305::new(&key);
aead.encrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)
}
pub fn encrypt_aead(
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
let mut out = body.to_vec();
Self::encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)?;
Ok(out)
}
pub fn crypt_in_place_no_auth(body: &mut Vec<u8>, nonce: &Nonce, shared_secret: &SharedSecret) {
let mut cipher = XChaCha20::new(shared_secret.into(), nonce.into());
cipher.apply_keystream(body);
}
pub fn crypt_b2b_no_auth(
in_buf: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) -> Vec<u8> {
let mut cipher = XChaCha20::new(shared_secret.into(), nonce.into());
// Allocate uninitialized memory, aligned to 8 byte boundary because capnp is faster this way
// and the Vec returned here will be used to hold decrypted rpc messages
let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) };
cipher.apply_keystream_b2b(in_buf, &mut out_buf).unwrap();
out_buf
}
pub fn crypt_no_auth(body: &[u8], nonce: &Nonce, shared_secret: &SharedSecret) -> Vec<u8> {
Self::crypt_b2b_no_auth(body, nonce, shared_secret)
}
}

View File

@ -0,0 +1,70 @@
use digest::generic_array::typenum::U64;
use digest::{Digest, Output};
use generic_array::GenericArray;
pub struct Blake3Digest512 {
dig: blake3::Hasher,
}
impl Digest for Blake3Digest512 {
type OutputSize = U64;
fn new() -> Self {
Self {
dig: blake3::Hasher::new(),
}
}
fn update(&mut self, data: impl AsRef<[u8]>) {
self.dig.update(data.as_ref());
}
fn chain(mut self, data: impl AsRef<[u8]>) -> Self
where
Self: Sized,
{
self.update(data);
self
}
fn finalize(self) -> Output<Self> {
let mut b = [0u8; 64];
self.dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
out
}
fn finalize_reset(&mut self) -> Output<Self> {
let mut b = [0u8; 64];
self.dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
self.reset();
out
}
fn reset(&mut self) {
self.dig.reset();
}
fn output_size() -> usize {
64
}
fn digest(data: &[u8]) -> Output<Self> {
let mut dig = blake3::Hasher::new();
dig.update(data);
let mut b = [0u8; 64];
dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
out
}
}

View File

@ -0,0 +1,300 @@
pub mod blake3digest512;
pub use blake3digest512::*;
use super::*;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use chacha20poly1305 as ch;
use chacha20poly1305::aead::{AeadInPlace, NewAead};
use core::convert::TryInto;
use curve25519_dalek as cd;
use digest::Digest;
use ed25519_dalek as ed;
use x25519_dalek as xd;
const AEAD_OVERHEAD: usize = 16;
pub const CRYPTO_KIND_VLD0: CryptoKind = FourCC([b'V', b'L', b'D', b'0']);
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, VeilidAPIError> {
let bytes = key.to_bytes();
let compressed = cd::edwards::CompressedEdwardsY(bytes);
let point = compressed
.decompress()
.ok_or_else(|| VeilidAPIError::internal("ed25519_to_x25519_pk failed"))?;
let mp = point.to_montgomery();
Ok(xd::PublicKey::from(mp.to_bytes()))
}
fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result<xd::StaticSecret, VeilidAPIError> {
let exp = ed::ExpandedSecretKey::from(key);
let bytes: [u8; ed::EXPANDED_SECRET_KEY_LENGTH] = exp.to_bytes();
let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(VeilidAPIError::internal)?;
Ok(xd::StaticSecret::from(lowbytes))
}
pub fn vld0_generate_keypair() -> KeyPair {
let mut csprng = VeilidRng {};
let keypair = ed::Keypair::generate(&mut csprng);
let dht_key = PublicKey::new(keypair.public.to_bytes());
let dht_key_secret = SecretKey::new(keypair.secret.to_bytes());
KeyPair::new(dht_key, dht_key_secret)
}
/// V0 CryptoSystem
#[derive(Clone)]
pub struct CryptoSystemVLD0 {
crypto: Crypto,
}
impl CryptoSystemVLD0 {
pub fn new(crypto: Crypto) -> Self {
Self { crypto }
}
}
impl CryptoSystem for CryptoSystemVLD0 {
// Accessors
fn kind(&self) -> CryptoKind {
CRYPTO_KIND_VLD0
}
fn crypto(&self) -> Crypto {
self.crypto.clone()
}
// Cached Operations
fn cached_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
self.crypto
.cached_dh_internal::<CryptoSystemVLD0>(self, key, secret)
}
// Generation
fn random_nonce(&self) -> Nonce {
let mut nonce = [0u8; 24];
random_bytes(&mut nonce).unwrap();
Nonce::new(nonce)
}
fn random_shared_secret(&self) -> SharedSecret {
let mut s = [0u8; 32];
random_bytes(&mut s).unwrap();
SharedSecret::new(s)
}
fn compute_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(VeilidAPIError::internal)?;
let pk_xd = ed25519_to_x25519_pk(&pk_ed)?;
let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(VeilidAPIError::internal)?;
let sk_xd = ed25519_to_x25519_sk(&sk_ed)?;
Ok(SharedSecret::new(sk_xd.diffie_hellman(&pk_xd).to_bytes()))
}
fn generate_keypair(&self) -> KeyPair {
vld0_generate_keypair()
}
fn generate_hash(&self, data: &[u8]) -> PublicKey {
PublicKey::new(*blake3::hash(data).as_bytes())
}
fn generate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
) -> Result<PublicKey, VeilidAPIError> {
let mut hasher = blake3::Hasher::new();
std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
Ok(PublicKey::new(*hasher.finalize().as_bytes()))
}
// Validation
fn validate_keypair(&self, dht_key: &PublicKey, dht_key_secret: &SecretKey) -> bool {
let data = vec![0u8; 512];
let sig = match self.sign(dht_key, dht_key_secret, &data) {
Ok(s) => s,
Err(_) => {
return false;
}
};
self.verify(dht_key, &data, &sig).is_ok()
}
fn validate_hash(&self, data: &[u8], dht_key: &PublicKey) -> bool {
let bytes = *blake3::hash(data).as_bytes();
bytes == dht_key.bytes
}
fn validate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
dht_key: &PublicKey,
) -> Result<bool, VeilidAPIError> {
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: &PublicKey, key2: &PublicKey) -> PublicKeyDistance {
let mut bytes = [0u8; PUBLIC_KEY_LENGTH];
for (n, byte) in bytes.iter_mut().enumerate() {
*byte = key1.bytes[n] ^ key2.bytes[n];
}
PublicKeyDistance::new(bytes)
}
// Authentication
fn sign(
&self,
dht_key: &PublicKey,
dht_key_secret: &SecretKey,
data: &[u8],
) -> Result<Signature, VeilidAPIError> {
let mut kpb: [u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH] =
[0u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH];
kpb[..SECRET_KEY_LENGTH].copy_from_slice(&dht_key_secret.bytes);
kpb[SECRET_KEY_LENGTH..].copy_from_slice(&dht_key.bytes);
let keypair = ed::Keypair::from_bytes(&kpb)
.map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?;
let mut dig = Blake3Digest512::new();
dig.update(data);
let sig = keypair
.sign_prehashed(dig, None)
.map_err(VeilidAPIError::internal)?;
let dht_sig = Signature::new(sig.to_bytes());
Ok(dht_sig)
}
fn verify(
&self,
dht_key: &PublicKey,
data: &[u8],
signature: &Signature,
) -> Result<(), VeilidAPIError> {
let pk = ed::PublicKey::from_bytes(&dht_key.bytes)
.map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?;
let sig = ed::Signature::from_bytes(&signature.bytes)
.map_err(|e| VeilidAPIError::parse_error("Signature is invalid", e))?;
let mut dig = Blake3Digest512::new();
dig.update(data);
pk.verify_prehashed(dig, None, &sig)
.map_err(|e| VeilidAPIError::parse_error("Verification failed", e))?;
Ok(())
}
// AEAD Encrypt/Decrypt
fn aead_overhead(&self) -> usize {
AEAD_OVERHEAD
}
fn decrypt_in_place_aead(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
let key = ch::Key::from(shared_secret.bytes);
let xnonce = ch::XNonce::from(nonce.bytes);
let aead = ch::XChaCha20Poly1305::new(&key);
aead.decrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)
}
fn decrypt_aead(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
let mut out = body.to_vec();
self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)?;
Ok(out)
}
fn encrypt_in_place_aead(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
let key = ch::Key::from(shared_secret.bytes);
let xnonce = ch::XNonce::from(nonce.bytes);
let aead = ch::XChaCha20Poly1305::new(&key);
aead.encrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)
}
fn encrypt_aead(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
let mut out = body.to_vec();
self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)?;
Ok(out)
}
// NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
) {
let mut cipher = XChaCha20::new(&shared_secret.bytes.into(), &nonce.bytes.into());
cipher.apply_keystream(body);
}
fn crypt_b2b_no_auth(
&self,
in_buf: &[u8],
out_buf: &mut [u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) {
let mut cipher = XChaCha20::new(&shared_secret.bytes.into(), &nonce.bytes.into());
cipher.apply_keystream_b2b(in_buf, out_buf).unwrap();
}
fn crypt_no_auth_aligned_8(
&self,
in_buf: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) -> Vec<u8> {
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);
out_buf
}
fn crypt_no_auth_unaligned(
&self,
in_buf: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) -> Vec<u8> {
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);
out_buf
}
}

View File

@ -3,60 +3,56 @@
use super::*;
use crate::*;
use core::convert::TryInto;
use data_encoding::BASE64URL_NOPAD;
// #[repr(C, packed)]
// struct ReceiptHeader {
// // Size is at least 8 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
// magic: [u8; 4], // 0x00: 0x52 0x43 0x50 0x54 ("RCPT")
// version: u8, // 0x04: 0 = ReceiptV0
// reserved: u8, // 0x05: Reserved for future use
// }
/// Out-of-band receipts are versioned along with envelope versions
///
/// These are the formats for the on-the-wire serialization performed by this module
///
/// #[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
/// magic: [u8; 3], // 0x00: 0x52 0x43 0x50 ("RCP")
/// version: u8, // 0x03: 0 = ReceiptV0
/// }
///
/// #[repr(C, packed)]
/// struct ReceiptV0 {
/// // Size is 66 bytes without extra data and signature, 130 with signature
/// magic: [u8; 3], // 0x00: 0x52 0x43 0x50 ("RCP")
/// version: u8, // 0x03: 0 = ReceiptV0
/// 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): Signature of the entire receipt including header and extra data is appended to the packet
/// }
// #[repr(C, packed)]
// struct ReceiptV0 {
// // Size is 106 bytes.
// magic: [u8; 4], // 0x00: 0x52 0x43 0x50 0x54 ("RCPT")
// version: u8, // 0x04: 0 = ReceiptV0
// reserved: u8, // 0x05: Reserved for future use
// size: u16, // 0x06: Total size of the receipt including the extra data and the signature. Maximum size is 1152 bytes.
// nonce: [u8; 24], // 0x08: 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], // 0x20: Node ID of the message source, which is the Ed25519 public key of the sender
// extra_data: [u8; ??], // 0x40: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1024 bytes)
// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature of the entire receipt including header and extra data is appended to the packet
// }
pub const MAX_RECEIPT_SIZE: usize = 1152;
pub const MAX_EXTRA_DATA_SIZE: usize = 1024;
pub const MIN_RECEIPT_SIZE: usize = 128;
pub const RECEIPT_MAGIC: &[u8; 4] = b"RCPT";
pub type ReceiptNonce = [u8; 24];
pub trait Encodable {
fn encode(&self) -> String;
}
impl Encodable for ReceiptNonce {
fn encode(&self) -> String {
BASE64URL_NOPAD.encode(self)
}
}
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;
pub const RECEIPT_MAGIC: &[u8; 3] = b"RCP";
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Receipt {
version: u8,
nonce: ReceiptNonce,
sender_id: DHTKey,
crypto_kind: CryptoKind,
nonce: Nonce,
sender_id: PublicKey,
extra_data: Vec<u8>,
}
impl Receipt {
pub fn try_new<D: AsRef<[u8]>>(
version: u8,
nonce: ReceiptNonce,
sender_id: DHTKey,
crypto_kind: CryptoKind,
nonce: Nonce,
sender_id: PublicKey,
extra_data: D,
) -> Result<Self, VeilidAPIError> {
assert!(VALID_ENVELOPE_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",
@ -65,20 +61,21 @@ impl Receipt {
}
Ok(Self {
version,
crypto_kind,
nonce,
sender_id,
extra_data: Vec::from(extra_data.as_ref()),
})
}
pub fn from_signed_data(data: &[u8]) -> Result<Receipt, VeilidAPIError> {
pub fn from_signed_data(crypto: Crypto, data: &[u8]) -> Result<Receipt, VeilidAPIError> {
// Ensure we are at least the length of the envelope
if data.len() < MIN_RECEIPT_SIZE {
apibail_parse_error!("receipt too small", data.len());
}
// Verify magic number
let magic: [u8; 4] = data[0x00..0x04]
let magic: [u8; 3] = data[0x00..0x03]
.try_into()
.map_err(VeilidAPIError::internal)?;
if magic != *RECEIPT_MAGIC {
@ -86,14 +83,24 @@ impl Receipt {
}
// Check version
let version = data[0x04];
if version > MAX_CRYPTO_VERSION || version < MIN_CRYPTO_VERSION {
apibail_parse_error!("unsupported cryptography version", version);
let version = data[0x03];
if !VALID_ENVELOPE_VERSIONS.contains(&version) {
apibail_parse_error!("unsupported envelope version", version);
}
// Check crypto kind
let crypto_kind = FourCC(
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[0x06..0x08]
data[0x08..0x0A]
.try_into()
.map_err(VeilidAPIError::internal)?,
);
@ -108,64 +115,80 @@ impl Receipt {
}
// Get sender id
let sender_id = DHTKey::new(
data[0x20..0x40]
let sender_id = PublicKey::new(
data[0x22..0x42]
.try_into()
.map_err(VeilidAPIError::internal)?,
);
// Get signature
let signature = DHTSignature::new(
let signature = Signature::new(
data[(data.len() - 64)..]
.try_into()
.map_err(VeilidAPIError::internal)?,
);
// Validate signature
verify(&sender_id, &data[0..(data.len() - 64)], &signature)
vcrypto
.verify(&sender_id, &data[0..(data.len() - 64)], &signature)
.map_err(VeilidAPIError::generic)?;
// Get nonce
let nonce: ReceiptNonce = data[0x08..0x20]
.try_into()
.map_err(VeilidAPIError::internal)?;
let nonce: Nonce = Nonce::new(
data[0x0A..0x22]
.try_into()
.map_err(VeilidAPIError::internal)?,
);
// Get extra data and signature
let extra_data: Vec<u8> = Vec::from(&data[0x40..(data.len() - 64)]);
let extra_data: Vec<u8> = Vec::from(&data[0x42..(data.len() - 64)]);
// Return receipt
Ok(Self {
version,
crypto_kind,
nonce,
sender_id,
extra_data,
})
}
pub fn to_signed_data(&self, secret: &DHTKeySecret) -> Result<Vec<u8>, VeilidAPIError> {
pub fn to_signed_data(
&self,
crypto: Crypto,
secret: &SecretKey,
) -> Result<Vec<u8>, VeilidAPIError> {
// 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<u8> = vec![0u8; receipt_size];
// Write magic
data[0x00..0x04].copy_from_slice(RECEIPT_MAGIC);
data[0x00..0x03].copy_from_slice(RECEIPT_MAGIC);
// Write version
data[0x04] = self.version;
data[0x03] = self.version;
// Write crypto kind
data[0x04..0x08].copy_from_slice(&self.crypto_kind.0);
// Write size
data[0x06..0x08].copy_from_slice(&(receipt_size as u16).to_le_bytes());
data[0x08..0x0A].copy_from_slice(&(receipt_size as u16).to_le_bytes());
// Write nonce
data[0x08..0x20].copy_from_slice(&self.nonce);
data[0x0A..0x22].copy_from_slice(&self.nonce.bytes);
// Write sender node id
data[0x20..0x40].copy_from_slice(&self.sender_id.bytes);
data[0x22..0x42].copy_from_slice(&self.sender_id.bytes);
// Write extra data
if !self.extra_data.is_empty() {
data[0x40..(receipt_size - 64)].copy_from_slice(self.extra_data.as_slice());
data[0x42..(receipt_size - 64)].copy_from_slice(self.extra_data.as_slice());
}
// Sign the receipt
let signature = sign(&self.sender_id, secret, &data[0..(receipt_size - 64)])
let signature = vcrypto
.sign(&self.sender_id, secret, &data[0..(receipt_size - 64)])
.map_err(VeilidAPIError::generic)?;
// Append the signature
data[(receipt_size - 64)..].copy_from_slice(&signature.bytes);
@ -177,11 +200,15 @@ impl Receipt {
self.version
}
pub fn get_nonce(&self) -> ReceiptNonce {
pub fn get_crypto_kind(&self) -> CryptoKind {
self.crypto_kind
}
pub fn get_nonce(&self) -> Nonce {
self.nonce
}
pub fn get_sender_id(&self) -> DHTKey {
pub fn get_sender_id(&self) -> PublicKey {
self.sender_id
}
pub fn get_extra_data(&self) -> &[u8] {

View File

@ -1,5 +1,21 @@
pub mod test_crypto;
pub mod test_dht_key;
pub mod test_envelope_receipt;
pub mod test_types;
use super::*;
use crate::tests::common::test_veilid_config::*;
async fn crypto_tests_startup() -> VeilidAPI {
trace!("crypto_tests: starting");
let (update_callback, config_callback) = setup_veilid_core();
let api = api_startup(update_callback, config_callback)
.await
.expect("startup failed");
api
}
async fn crypto_tests_shutdown(api: VeilidAPI) {
trace!("crypto_tests: shutting down");
api.shutdown().await;
trace!("crypto_tests: finished");
}

View File

@ -1,37 +1,21 @@
use super::*;
use crate::tests::common::test_veilid_config::*;
static LOREM_IPSUM:&[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
async fn startup() -> VeilidAPI {
trace!("test_table_store: starting");
let (update_callback, config_callback) = setup_veilid_core();
let api = api_startup(update_callback, config_callback)
.await
.expect("startup failed");
api
}
async fn shutdown(api: VeilidAPI) {
trace!("test_table_store: shutting down");
api.shutdown().await;
trace!("test_table_store: finished");
}
pub async fn test_aead() {
pub async fn test_aead(vcrypto: CryptoSystemVersion) {
trace!("test_aead");
let n1 = Crypto::get_random_nonce();
let n1 = vcrypto.random_nonce();
let n2 = loop {
let n = Crypto::get_random_nonce();
let n = vcrypto.random_nonce();
if n != n1 {
break n;
}
};
let ss1 = Crypto::get_random_secret();
let ss1 = vcrypto.random_shared_secret();
let ss2 = loop {
let ss = Crypto::get_random_secret();
let ss = vcrypto.random_shared_secret();
if ss != ss1 {
break ss;
}
@ -41,67 +25,77 @@ pub async fn test_aead() {
let body2 = body.clone();
let size_before_encrypt = body.len();
assert!(
Crypto::encrypt_in_place_aead(&mut body, &n1, &ss1, None).is_ok(),
vcrypto
.encrypt_in_place_aead(&mut body, &n1, &ss1, None)
.is_ok(),
"encrypt should succeed"
);
let size_after_encrypt = body.len();
assert!(
size_after_encrypt - size_before_encrypt == AEAD_OVERHEAD,
size_after_encrypt - size_before_encrypt == vcrypto.aead_overhead(),
"overhead should match"
);
let mut body3 = body.clone();
let mut body4 = body.clone();
let mut body5 = body.clone();
assert!(
Crypto::decrypt_in_place_aead(&mut body, &n1, &ss1, None).is_ok(),
vcrypto
.decrypt_in_place_aead(&mut body, &n1, &ss1, None)
.is_ok(),
"decrypt should succeed"
);
assert_eq!(body, body2, "results should be the same");
assert!(
Crypto::decrypt_in_place_aead(&mut body3, &n2, &ss1, None).is_err(),
vcrypto
.decrypt_in_place_aead(&mut body3, &n2, &ss1, None)
.is_err(),
"decrypt with wrong nonce should fail"
);
assert_ne!(body3, body, "failure changes data");
assert!(
Crypto::decrypt_in_place_aead(&mut body4, &n1, &ss2, None).is_err(),
vcrypto
.decrypt_in_place_aead(&mut body4, &n1, &ss2, None)
.is_err(),
"decrypt with wrong secret should fail"
);
assert_ne!(body4, body, "failure changes data");
assert!(
Crypto::decrypt_in_place_aead(&mut body5, &n1, &ss2, Some(b"foobar")).is_err(),
vcrypto
.decrypt_in_place_aead(&mut body5, &n1, &ss2, Some(b"foobar"))
.is_err(),
"decrypt with wrong associated data should fail"
);
assert_ne!(body5, body, "failure changes data");
assert!(
Crypto::decrypt_aead(LOREM_IPSUM, &n1, &ss1, None).is_err(),
vcrypto.decrypt_aead(LOREM_IPSUM, &n1, &ss1, None).is_err(),
"should fail authentication"
);
let body5 = Crypto::encrypt_aead(LOREM_IPSUM, &n1, &ss1, None).unwrap();
let body6 = Crypto::decrypt_aead(&body5, &n1, &ss1, None).unwrap();
let body7 = Crypto::encrypt_aead(LOREM_IPSUM, &n1, &ss1, None).unwrap();
let body5 = vcrypto.encrypt_aead(LOREM_IPSUM, &n1, &ss1, None).unwrap();
let body6 = vcrypto.decrypt_aead(&body5, &n1, &ss1, None).unwrap();
let body7 = vcrypto.encrypt_aead(LOREM_IPSUM, &n1, &ss1, None).unwrap();
assert_eq!(body6, LOREM_IPSUM);
assert_eq!(body5, body7);
}
pub async fn test_no_auth() {
pub async fn test_no_auth(vcrypto: CryptoSystemVersion) {
trace!("test_no_auth");
let n1 = Crypto::get_random_nonce();
let n1 = vcrypto.random_nonce();
let n2 = loop {
let n = Crypto::get_random_nonce();
let n = vcrypto.random_nonce();
if n != n1 {
break n;
}
};
let ss1 = Crypto::get_random_secret();
let ss1 = vcrypto.random_shared_secret();
let ss2 = loop {
let ss = Crypto::get_random_secret();
let ss = vcrypto.random_shared_secret();
if ss != ss1 {
break ss;
}
@ -110,7 +104,7 @@ pub async fn test_no_auth() {
let mut body = LOREM_IPSUM.to_vec();
let body2 = body.clone();
let size_before_encrypt = body.len();
Crypto::crypt_in_place_no_auth(&mut body, &n1, &ss1);
vcrypto.crypt_in_place_no_auth(&mut body, &n1, &ss1);
let size_after_encrypt = body.len();
assert_eq!(
@ -120,41 +114,47 @@ pub async fn test_no_auth() {
let mut body3 = body.clone();
let mut body4 = body.clone();
Crypto::crypt_in_place_no_auth(&mut body, &n1, &ss1);
vcrypto.crypt_in_place_no_auth(&mut body, &n1, &ss1);
assert_eq!(body, body2, "result after decrypt should be the same");
Crypto::crypt_in_place_no_auth(&mut body3, &n2, &ss1);
vcrypto.crypt_in_place_no_auth(&mut body3, &n2, &ss1);
assert_ne!(body3, body, "decrypt should not be equal with wrong nonce");
Crypto::crypt_in_place_no_auth(&mut body4, &n1, &ss2);
vcrypto.crypt_in_place_no_auth(&mut body4, &n1, &ss2);
assert_ne!(body4, body, "decrypt should not be equal with wrong secret");
let body5 = Crypto::crypt_no_auth(LOREM_IPSUM, &n1, &ss1);
let body6 = Crypto::crypt_no_auth(&body5, &n1, &ss1);
let body7 = Crypto::crypt_no_auth(LOREM_IPSUM, &n1, &ss1);
let body5 = vcrypto.crypt_no_auth_unaligned(LOREM_IPSUM, &n1, &ss1);
let body6 = vcrypto.crypt_no_auth_unaligned(&body5, &n1, &ss1);
let body7 = vcrypto.crypt_no_auth_unaligned(LOREM_IPSUM, &n1, &ss1);
assert_eq!(body6, LOREM_IPSUM);
assert_eq!(body5, body7);
let body5 = vcrypto.crypt_no_auth_aligned_8(LOREM_IPSUM, &n1, &ss1);
let body6 = vcrypto.crypt_no_auth_aligned_8(&body5, &n1, &ss1);
let body7 = vcrypto.crypt_no_auth_aligned_8(LOREM_IPSUM, &n1, &ss1);
assert_eq!(body6, LOREM_IPSUM);
assert_eq!(body5, body7);
}
pub async fn test_dh(crypto: Crypto) {
pub async fn test_dh(vcrypto: CryptoSystemVersion) {
trace!("test_dh");
let (dht_key, dht_key_secret) = key::generate_secret();
let (dht_key2, dht_key_secret2) = key::generate_secret();
let (dht_key, dht_key_secret) = vcrypto.generate_keypair().into_split();
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split();
let r1 = Crypto::compute_dh(&dht_key, &dht_key_secret2).unwrap();
let r2 = Crypto::compute_dh(&dht_key2, &dht_key_secret).unwrap();
let r3 = Crypto::compute_dh(&dht_key, &dht_key_secret2).unwrap();
let r4 = Crypto::compute_dh(&dht_key2, &dht_key_secret).unwrap();
let r1 = vcrypto.compute_dh(&dht_key, &dht_key_secret2).unwrap();
let r2 = vcrypto.compute_dh(&dht_key2, &dht_key_secret).unwrap();
let r3 = vcrypto.compute_dh(&dht_key, &dht_key_secret2).unwrap();
let r4 = vcrypto.compute_dh(&dht_key2, &dht_key_secret).unwrap();
assert_eq!(r1, r2);
assert_eq!(r3, r4);
assert_eq!(r2, r3);
trace!("dh: {:?}", r1);
// test cache
let r5 = crypto.cached_dh(&dht_key, &dht_key_secret2).unwrap();
let r6 = crypto.cached_dh(&dht_key2, &dht_key_secret).unwrap();
let r7 = crypto.cached_dh(&dht_key, &dht_key_secret2).unwrap();
let r8 = crypto.cached_dh(&dht_key2, &dht_key_secret).unwrap();
let r5 = vcrypto.cached_dh(&dht_key, &dht_key_secret2).unwrap();
let r6 = vcrypto.cached_dh(&dht_key2, &dht_key_secret).unwrap();
let r7 = vcrypto.cached_dh(&dht_key, &dht_key_secret2).unwrap();
let r8 = vcrypto.cached_dh(&dht_key2, &dht_key_secret).unwrap();
assert_eq!(r1, r5);
assert_eq!(r2, r6);
assert_eq!(r3, r7);
@ -163,11 +163,17 @@ pub async fn test_dh(crypto: Crypto) {
}
pub async fn test_all() {
let api = startup().await;
let api = crypto_tests_startup().await;
let crypto = api.crypto().unwrap();
test_aead().await;
test_no_auth().await;
test_dh(crypto).await;
shutdown(api.clone()).await;
// Test versions
for v in VALID_CRYPTO_KINDS {
let vcrypto = crypto.get(v).unwrap();
test_aead(vcrypto.clone()).await;
test_no_auth(vcrypto.clone()).await;
test_dh(vcrypto).await;
}
crypto_tests_shutdown(api.clone()).await;
assert!(api.is_shutdown());
}

View File

@ -1,304 +0,0 @@
#![allow(clippy::bool_assert_comparison)]
use super::*;
use core::convert::TryFrom;
static LOREM_IPSUM:&str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
static CHEEZBURGER: &str = "I can has cheezburger";
static EMPTY_KEY: [u8; key::DHT_KEY_LENGTH] = [0u8; key::DHT_KEY_LENGTH];
static EMPTY_KEY_SECRET: [u8; key::DHT_KEY_SECRET_LENGTH] = [0u8; key::DHT_KEY_SECRET_LENGTH];
pub async fn test_generate_secret() {
// Verify keys generate
let (dht_key, dht_key_secret) = key::generate_secret();
let (dht_key2, dht_key_secret2) = key::generate_secret();
// 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);
// Verify the keys and secrets are different across keypairs
assert_ne!(dht_key, dht_key2);
assert_ne!(dht_key_secret, dht_key_secret2);
}
pub async fn test_sign_and_verify() {
// Make two keys
let (dht_key, dht_key_secret) = key::generate_secret();
let (dht_key2, dht_key_secret2) = key::generate_secret();
// Sign the same message twice
let dht_sig = key::sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes()).unwrap();
trace!("dht_sig: {:?}", dht_sig);
let dht_sig_b = key::sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes()).unwrap();
// Sign a second message
let dht_sig_c = key::sign(&dht_key, &dht_key_secret, CHEEZBURGER.as_bytes()).unwrap();
trace!("dht_sig_c: {:?}", dht_sig_c);
// Verify they are the same signature
assert_eq!(dht_sig, dht_sig_b);
// Sign the same message with a different key
let dht_sig2 = key::sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM.as_bytes()).unwrap();
// Verify a different key gives a different signature
assert_ne!(dht_sig2, dht_sig_b);
// Try using the wrong secret to sign
let a1 = key::sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes()).unwrap();
let a2 = key::sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM.as_bytes()).unwrap();
let b1 = key::sign(&dht_key, &dht_key_secret2, LOREM_IPSUM.as_bytes()).unwrap();
let b2 = key::sign(&dht_key2, &dht_key_secret, LOREM_IPSUM.as_bytes()).unwrap();
assert_ne!(a1, b1);
assert_ne!(a2, b2);
assert_ne!(a1, b2);
assert_ne!(a2, b1);
assert_ne!(a1, a2);
assert_ne!(b1, b2);
assert_ne!(a1, b2);
assert_ne!(b1, a2);
assert_eq!(key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &a1), Ok(()));
assert_eq!(key::verify(&dht_key2, LOREM_IPSUM.as_bytes(), &a2), Ok(()));
assert!(key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &b1).is_err());
assert!(key::verify(&dht_key2, LOREM_IPSUM.as_bytes(), &b2).is_err());
// Try verifications that should work
assert_eq!(
key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig),
Ok(())
);
assert_eq!(
key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig_b),
Ok(())
);
assert_eq!(
key::verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig2),
Ok(())
);
assert_eq!(
key::verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig_c),
Ok(())
);
// Try verifications that shouldn't work
assert!(key::verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig).is_err());
assert!(key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig2).is_err());
assert!(key::verify(&dht_key2, CHEEZBURGER.as_bytes(), &dht_sig_c).is_err());
assert!(key::verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig).is_err());
}
pub async fn test_key_conversions() {
// Test default key
let (dht_key, dht_key_secret) = (key::DHTKey::default(), key::DHTKeySecret::default());
assert_eq!(dht_key.bytes, EMPTY_KEY);
assert_eq!(dht_key_secret.bytes, EMPTY_KEY_SECRET);
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 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);
// Make different keys
let (dht_key2, dht_key_secret2) = key::generate_secret();
trace!("dht_key2: {:?}", dht_key2);
trace!("dht_key_secret2: {:?}", dht_key_secret2);
let (dht_key3, _dht_key_secret3) = key::generate_secret();
trace!("dht_key3: {:?}", dht_key3);
trace!("_dht_key_secret3: {:?}", _dht_key_secret3);
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);
// Assert they convert back correctly
let dht_key_back = key::DHTKey::try_from(dht_key_string.as_str()).unwrap();
let dht_key_back2 = key::DHTKey::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 dht_key_secret_back = key::DHTKeySecret::try_from(dht_key_secret_string.as_str()).unwrap();
assert_eq!(dht_key_secret_back, dht_key_secret);
let dht_key2_back = key::DHTKey::try_from(dht_key2_string.as_str()).unwrap();
let dht_key2_back2 = key::DHTKey::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 dht_key_secret2_back =
key::DHTKeySecret::try_from(dht_key_secret2_string.as_str()).unwrap();
assert_eq!(dht_key_secret2_back, dht_key_secret2);
// Assert string roundtrip
assert_eq!(String::from(&dht_key2_back), dht_key2_string);
// These conversions should fail
assert!(key::DHTKey::try_from("whatever").is_err());
assert!(key::DHTKeySecret::try_from("whatever").is_err());
assert!(key::DHTKey::try_from("").is_err());
assert!(key::DHTKeySecret::try_from("").is_err());
assert!(key::DHTKey::try_from(" ").is_err());
assert!(key::DHTKeySecret::try_from(" ").is_err());
assert!(key::DHTKey::try_from(
"qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
)
.is_err());
assert!(key::DHTKeySecret::try_from(
"qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
)
.is_err());
}
pub async fn test_encode_decode() {
let dht_key = key::DHTKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
let dht_key_secret =
key::DHTKeySecret::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
let dht_key_b = key::DHTKey::new(EMPTY_KEY);
let dht_key_secret_b = key::DHTKeySecret::new(EMPTY_KEY_SECRET);
assert_eq!(dht_key, dht_key_b);
assert_eq!(dht_key_secret, dht_key_secret_b);
let (dht_key2, dht_key_secret2) = key::generate_secret();
let e1 = dht_key.encode();
trace!("e1: {:?}", e1);
assert_eq!(e1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned());
let e1s = dht_key_secret.encode();
trace!("e1s: {:?}", e1s);
let e2 = dht_key2.encode();
trace!("e2: {:?}", e2);
let e2s = dht_key_secret2.encode();
trace!("e2s: {:?}", e2s);
let d1 = key::DHTKey::try_decode(e1.as_str()).unwrap();
trace!("d1: {:?}", d1);
assert_eq!(dht_key, d1);
let d1s = key::DHTKeySecret::try_decode(e1s.as_str()).unwrap();
trace!("d1s: {:?}", d1s);
assert_eq!(dht_key_secret, d1s);
let d2 = key::DHTKey::try_decode(e2.as_str()).unwrap();
trace!("d2: {:?}", d2);
assert_eq!(dht_key2, d2);
let d2s = key::DHTKeySecret::try_decode(e2s.as_str()).unwrap();
trace!("d2s: {:?}", d2s);
assert_eq!(dht_key_secret2, d2s);
// Failures
let f1 = key::DHTKeySecret::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
assert!(f1.is_err());
let f2 = key::DHTKeySecret::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&");
assert!(f2.is_err());
}
async fn test_hash() {
let mut s = BTreeSet::<key::DHTKey>::new();
let k1 = key::generate_hash("abc".as_bytes());
let k2 = key::generate_hash("abcd".as_bytes());
let k3 = key::generate_hash("".as_bytes());
let k4 = key::generate_hash(" ".as_bytes());
let k5 = key::generate_hash(LOREM_IPSUM.as_bytes());
let k6 = key::generate_hash(CHEEZBURGER.as_bytes());
s.insert(k1);
s.insert(k2);
s.insert(k3);
s.insert(k4);
s.insert(k5);
s.insert(k6);
assert_eq!(s.len(), 6);
let v1 = key::generate_hash("abc".as_bytes());
let v2 = key::generate_hash("abcd".as_bytes());
let v3 = key::generate_hash("".as_bytes());
let v4 = key::generate_hash(" ".as_bytes());
let v5 = key::generate_hash(LOREM_IPSUM.as_bytes());
let v6 = key::generate_hash(CHEEZBURGER.as_bytes());
assert_eq!(k1, v1);
assert_eq!(k2, v2);
assert_eq!(k3, v3);
assert_eq!(k4, v4);
assert_eq!(k5, v5);
assert_eq!(k6, v6);
key::validate_hash("abc".as_bytes(), &v1);
key::validate_hash("abcd".as_bytes(), &v2);
key::validate_hash("".as_bytes(), &v3);
key::validate_hash(" ".as_bytes(), &v4);
key::validate_hash(LOREM_IPSUM.as_bytes(), &v5);
key::validate_hash(CHEEZBURGER.as_bytes(), &v6);
}
async fn test_operations() {
let k1 = key::generate_hash(LOREM_IPSUM.as_bytes());
let k2 = key::generate_hash(CHEEZBURGER.as_bytes());
let k3 = key::generate_hash("abc".as_bytes());
// Get distance
let d1 = key::distance(&k1, &k2);
let d2 = key::distance(&k2, &k1);
let d3 = key::distance(&k1, &k3);
let d4 = key::distance(&k2, &k3);
trace!("d1={:?}", d1);
trace!("d2={:?}", d2);
trace!("d3={:?}", d3);
trace!("d4={:?}", d4);
// Verify commutativity
assert_eq!(d1, d2);
assert!(d1 <= d2);
assert!(d1 >= d2);
assert!(d1 >= d2);
assert!(d1 <= d2);
assert_eq!(d2, d1);
assert!(d2 <= d1);
assert!(d2 >= d1);
assert!(d2 >= d1);
assert!(d2 <= d1);
// Verify nibbles
assert_eq!(d1.nibble(0), 0x9u8);
assert_eq!(d1.nibble(1), 0x4u8);
assert_eq!(d1.nibble(2), 0x3u8);
assert_eq!(d1.nibble(3), 0x6u8);
assert_eq!(d1.nibble(63), 0x6u8);
assert_eq!(d1.first_nonzero_nibble(), Some((0, 0x9u8)));
assert_eq!(d2.first_nonzero_nibble(), Some((0, 0x9u8)));
assert_eq!(d3.first_nonzero_nibble(), Some((1, 0x4u8)));
assert_eq!(d4.first_nonzero_nibble(), Some((0, 0x9u8)));
// Verify bits
assert_eq!(d1.bit(0), true);
assert_eq!(d1.bit(1), false);
assert_eq!(d1.bit(7), false);
assert_eq!(d1.bit(8), false);
assert_eq!(d1.bit(14), true);
assert_eq!(d1.bit(15), false);
assert_eq!(d1.bit(254), true);
assert_eq!(d1.bit(255), false);
assert_eq!(d1.first_nonzero_bit(), Some(0));
assert_eq!(d2.first_nonzero_bit(), Some(0));
assert_eq!(d3.first_nonzero_bit(), Some(5));
assert_eq!(d4.first_nonzero_bit(), Some(0));
}
pub async fn test_all() {
test_generate_secret().await;
test_sign_and_verify().await;
test_key_conversions().await;
test_encode_decode().await;
test_hash().await;
test_operations().await;
}

View File

@ -1,37 +1,39 @@
use super::*;
use crate::tests::common::test_veilid_config::*;
pub async fn test_envelope_round_trip() {
pub async fn test_envelope_round_trip(
envelope_version: EnvelopeVersion,
vcrypto: CryptoSystemVersion,
) {
info!("--- test envelope round trip ---");
let (update_callback, config_callback) = setup_veilid_core();
let api = api_startup(update_callback, config_callback)
.await
.expect("startup failed");
// Get crypto
let crypto = api.crypto().unwrap();
// Create envelope
let ts = Timestamp::from(0x12345678ABCDEF69u64);
let nonce = Crypto::get_random_nonce();
let (sender_id, sender_secret) = generate_secret();
let (recipient_id, recipient_secret) = generate_secret();
let envelope = Envelope::new(0, ts, nonce, sender_id, recipient_id);
let nonce = vcrypto.random_nonce();
let (sender_id, sender_secret) = vcrypto.generate_keypair().into_split();
let (recipient_id, recipient_secret) = vcrypto.generate_keypair().into_split();
let envelope = Envelope::new(
envelope_version,
vcrypto.kind(),
ts,
nonce,
sender_id,
recipient_id,
);
// Create arbitrary body
let body = b"This is an arbitrary body";
// Serialize to bytes
let enc_data = envelope
.to_encrypted_data(crypto.clone(), body, &sender_secret)
.to_encrypted_data(vcrypto.crypto(), body, &sender_secret)
.expect("failed to encrypt data");
// Deserialize from bytes
let envelope2 =
Envelope::from_signed_data(&enc_data).expect("failed to deserialize envelope from data");
let envelope2 = Envelope::from_signed_data(vcrypto.crypto(), &enc_data)
.expect("failed to deserialize envelope from data");
let body2 = envelope2
.decrypt_body(crypto.clone(), &enc_data, &recipient_secret)
.decrypt_body(vcrypto.crypto(), &enc_data, &recipient_secret)
.expect("failed to decrypt envelope body");
// Compare envelope and body
@ -43,41 +45,43 @@ 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(&mod_enc_data).is_err(),
Envelope::from_signed_data(vcrypto.crypto(), &mod_enc_data).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(&mod_enc_data2).is_err(),
Envelope::from_signed_data(vcrypto.crypto(), &mod_enc_data2).is_err(),
"should have failed to decode envelope with modified data"
);
api.shutdown().await;
}
pub async fn test_receipt_round_trip() {
pub async fn test_receipt_round_trip(
envelope_version: EnvelopeVersion,
vcrypto: CryptoSystemVersion,
) {
info!("--- test receipt round trip ---");
// Create arbitrary body
let body = b"This is an arbitrary body";
// Create receipt
let nonce = Crypto::get_random_nonce();
let (sender_id, sender_secret) = generate_secret();
let receipt = Receipt::try_new(0, nonce, sender_id, body).expect("should not fail");
let nonce = vcrypto.random_nonce();
let (sender_id, sender_secret) = vcrypto.generate_keypair().into_split();
let receipt = Receipt::try_new(envelope_version, vcrypto.kind(), nonce, sender_id, body)
.expect("should not fail");
// Serialize to bytes
let mut enc_data = receipt
.to_signed_data(&sender_secret)
.to_signed_data(vcrypto.crypto(), &sender_secret)
.expect("failed to make signed data");
// Deserialize from bytes
let receipt2 =
Receipt::from_signed_data(&enc_data).expect("failed to deserialize envelope from data");
let receipt2 = Receipt::from_signed_data(vcrypto.crypto(), &enc_data)
.expect("failed to deserialize envelope from data");
// Should not validate even when a single bit is changed
enc_data[5] = 0x01;
Receipt::from_signed_data(&enc_data)
Receipt::from_signed_data(vcrypto.crypto(), &enc_data)
.expect_err("should have failed to decrypt using wrong secret");
// Compare receipts
@ -85,6 +89,19 @@ pub async fn test_receipt_round_trip() {
}
pub async fn test_all() {
test_envelope_round_trip().await;
test_receipt_round_trip().await;
let api = crypto_tests_startup().await;
let crypto = api.crypto().unwrap();
// Test versions
for ev in VALID_ENVELOPE_VERSIONS {
for v in VALID_CRYPTO_KINDS {
let vcrypto = crypto.get(v).unwrap();
test_envelope_round_trip(ev, vcrypto.clone()).await;
test_receipt_round_trip(ev, vcrypto).await;
}
}
crypto_tests_shutdown(api.clone()).await;
assert!(api.is_shutdown());
}

View File

@ -0,0 +1,348 @@
#![allow(clippy::bool_assert_comparison)]
use super::*;
use core::convert::TryFrom;
static LOREM_IPSUM:&str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
static CHEEZBURGER: &str = "I can has cheezburger";
static EMPTY_KEY: [u8; PUBLIC_KEY_LENGTH] = [0u8; PUBLIC_KEY_LENGTH];
static EMPTY_KEY_SECRET: [u8; SECRET_KEY_LENGTH] = [0u8; SECRET_KEY_LENGTH];
pub async fn test_generate_secret(vcrypto: CryptoSystemVersion) {
// Verify keys generate
let (dht_key, dht_key_secret) = vcrypto.generate_keypair().into_split();
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().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);
// Verify the keys and secrets are different across keypairs
assert_ne!(dht_key, dht_key2);
assert_ne!(dht_key_secret, dht_key_secret2);
}
pub async fn test_sign_and_verify(vcrypto: CryptoSystemVersion) {
// Make two keys
let (dht_key, dht_key_secret) = vcrypto.generate_keypair().into_split();
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split();
// Sign the same message twice
let dht_sig = vcrypto
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes())
.unwrap();
trace!("dht_sig: {:?}", dht_sig);
let dht_sig_b = vcrypto
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes())
.unwrap();
// Sign a second message
let dht_sig_c = vcrypto
.sign(&dht_key, &dht_key_secret, CHEEZBURGER.as_bytes())
.unwrap();
trace!("dht_sig_c: {:?}", dht_sig_c);
// Verify they are the same signature
assert_eq!(dht_sig, dht_sig_b);
// Sign the same message with a different key
let dht_sig2 = vcrypto
.sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM.as_bytes())
.unwrap();
// Verify a different key gives a different signature
assert_ne!(dht_sig2, dht_sig_b);
// Try using the wrong secret to sign
let a1 = vcrypto
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes())
.unwrap();
let a2 = vcrypto
.sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM.as_bytes())
.unwrap();
let b1 = vcrypto
.sign(&dht_key, &dht_key_secret2, LOREM_IPSUM.as_bytes())
.unwrap();
let b2 = vcrypto
.sign(&dht_key2, &dht_key_secret, LOREM_IPSUM.as_bytes())
.unwrap();
assert_ne!(a1, b1);
assert_ne!(a2, b2);
assert_ne!(a1, b2);
assert_ne!(a2, b1);
assert_ne!(a1, a2);
assert_ne!(b1, b2);
assert_ne!(a1, b2);
assert_ne!(b1, a2);
assert_eq!(
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &a1),
Ok(())
);
assert_eq!(
vcrypto.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &a2),
Ok(())
);
assert!(vcrypto
.verify(&dht_key, LOREM_IPSUM.as_bytes(), &b1)
.is_err());
assert!(vcrypto
.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &b2)
.is_err());
// Try verifications that should work
assert_eq!(
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig),
Ok(())
);
assert_eq!(
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig_b),
Ok(())
);
assert_eq!(
vcrypto.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig2),
Ok(())
);
assert_eq!(
vcrypto.verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig_c),
Ok(())
);
// Try verifications that shouldn't work
assert!(vcrypto
.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig)
.is_err());
assert!(vcrypto
.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig2)
.is_err());
assert!(vcrypto
.verify(&dht_key2, CHEEZBURGER.as_bytes(), &dht_sig_c)
.is_err());
assert!(vcrypto
.verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig)
.is_err());
}
pub async fn test_key_conversions(vcrypto: CryptoSystemVersion) {
// Test default key
let (dht_key, dht_key_secret) = (PublicKey::default(), SecretKey::default());
assert_eq!(dht_key.bytes, EMPTY_KEY);
assert_eq!(dht_key_secret.bytes, EMPTY_KEY_SECRET);
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 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);
// Make different keys
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split();
trace!("dht_key2: {:?}", dht_key2);
trace!("dht_key_secret2: {:?}", dht_key_secret2);
let (dht_key3, _dht_key_secret3) = vcrypto.generate_keypair().into_split();
trace!("dht_key3: {:?}", dht_key3);
trace!("_dht_key_secret3: {:?}", _dht_key_secret3);
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);
// Assert they convert back correctly
let dht_key_back = PublicKey::try_from(dht_key_string.as_str()).unwrap();
let dht_key_back2 = PublicKey::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 dht_key_secret_back = SecretKey::try_from(dht_key_secret_string.as_str()).unwrap();
assert_eq!(dht_key_secret_back, dht_key_secret);
let dht_key2_back = PublicKey::try_from(dht_key2_string.as_str()).unwrap();
let dht_key2_back2 = PublicKey::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 dht_key_secret2_back = SecretKey::try_from(dht_key_secret2_string.as_str()).unwrap();
assert_eq!(dht_key_secret2_back, dht_key_secret2);
// Assert string roundtrip
assert_eq!(String::from(&dht_key2_back), dht_key2_string);
// These conversions should fail
assert!(PublicKey::try_from("whatever").is_err());
assert!(SecretKey::try_from("whatever").is_err());
assert!(PublicKey::try_from("").is_err());
assert!(SecretKey::try_from("").is_err());
assert!(PublicKey::try_from(" ").is_err());
assert!(SecretKey::try_from(" ").is_err());
assert!(PublicKey::try_from(
"qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
)
.is_err());
assert!(SecretKey::try_from(
"qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
)
.is_err());
}
pub async fn test_encode_decode(vcrypto: CryptoSystemVersion) {
let dht_key = PublicKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
let dht_key_secret =
SecretKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
let dht_key_b = PublicKey::new(EMPTY_KEY);
let dht_key_secret_b = SecretKey::new(EMPTY_KEY_SECRET);
assert_eq!(dht_key, dht_key_b);
assert_eq!(dht_key_secret, dht_key_secret_b);
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split();
let e1 = dht_key.encode();
trace!("e1: {:?}", e1);
assert_eq!(e1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned());
let e1s = dht_key_secret.encode();
trace!("e1s: {:?}", e1s);
let e2 = dht_key2.encode();
trace!("e2: {:?}", e2);
let e2s = dht_key_secret2.encode();
trace!("e2s: {:?}", e2s);
let d1 = PublicKey::try_decode(e1.as_str()).unwrap();
trace!("d1: {:?}", d1);
assert_eq!(dht_key, d1);
let d1s = SecretKey::try_decode(e1s.as_str()).unwrap();
trace!("d1s: {:?}", d1s);
assert_eq!(dht_key_secret, d1s);
let d2 = PublicKey::try_decode(e2.as_str()).unwrap();
trace!("d2: {:?}", d2);
assert_eq!(dht_key2, d2);
let d2s = SecretKey::try_decode(e2s.as_str()).unwrap();
trace!("d2s: {:?}", d2s);
assert_eq!(dht_key_secret2, d2s);
// Failures
let f1 = SecretKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
assert!(f1.is_err());
let f2 = SecretKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&");
assert!(f2.is_err());
}
async fn test_hash(vcrypto: CryptoSystemVersion) {
let mut s = BTreeSet::<PublicKey>::new();
let k1 = vcrypto.generate_hash("abc".as_bytes());
let k2 = vcrypto.generate_hash("abcd".as_bytes());
let k3 = vcrypto.generate_hash("".as_bytes());
let k4 = vcrypto.generate_hash(" ".as_bytes());
let k5 = vcrypto.generate_hash(LOREM_IPSUM.as_bytes());
let k6 = vcrypto.generate_hash(CHEEZBURGER.as_bytes());
s.insert(k1);
s.insert(k2);
s.insert(k3);
s.insert(k4);
s.insert(k5);
s.insert(k6);
assert_eq!(s.len(), 6);
let v1 = vcrypto.generate_hash("abc".as_bytes());
let v2 = vcrypto.generate_hash("abcd".as_bytes());
let v3 = vcrypto.generate_hash("".as_bytes());
let v4 = vcrypto.generate_hash(" ".as_bytes());
let v5 = vcrypto.generate_hash(LOREM_IPSUM.as_bytes());
let v6 = vcrypto.generate_hash(CHEEZBURGER.as_bytes());
assert_eq!(k1, v1);
assert_eq!(k2, v2);
assert_eq!(k3, v3);
assert_eq!(k4, v4);
assert_eq!(k5, v5);
assert_eq!(k6, v6);
vcrypto.validate_hash("abc".as_bytes(), &v1);
vcrypto.validate_hash("abcd".as_bytes(), &v2);
vcrypto.validate_hash("".as_bytes(), &v3);
vcrypto.validate_hash(" ".as_bytes(), &v4);
vcrypto.validate_hash(LOREM_IPSUM.as_bytes(), &v5);
vcrypto.validate_hash(CHEEZBURGER.as_bytes(), &v6);
}
async fn test_operations(vcrypto: CryptoSystemVersion) {
let k1 = vcrypto.generate_hash(LOREM_IPSUM.as_bytes());
let k2 = vcrypto.generate_hash(CHEEZBURGER.as_bytes());
let k3 = vcrypto.generate_hash("abc".as_bytes());
// Get distance
let d1 = vcrypto.distance(&k1, &k2);
let d2 = vcrypto.distance(&k2, &k1);
let d3 = vcrypto.distance(&k1, &k3);
let d4 = vcrypto.distance(&k2, &k3);
trace!("d1={:?}", d1);
trace!("d2={:?}", d2);
trace!("d3={:?}", d3);
trace!("d4={:?}", d4);
// Verify commutativity
assert_eq!(d1, d2);
assert!(d1 <= d2);
assert!(d1 >= d2);
assert!(d1 >= d2);
assert!(d1 <= d2);
assert_eq!(d2, d1);
assert!(d2 <= d1);
assert!(d2 >= d1);
assert!(d2 >= d1);
assert!(d2 <= d1);
// Verify nibbles
assert_eq!(d1.nibble(0), 0x9u8);
assert_eq!(d1.nibble(1), 0x4u8);
assert_eq!(d1.nibble(2), 0x3u8);
assert_eq!(d1.nibble(3), 0x6u8);
assert_eq!(d1.nibble(63), 0x6u8);
assert_eq!(d1.first_nonzero_nibble(), Some((0, 0x9u8)));
assert_eq!(d2.first_nonzero_nibble(), Some((0, 0x9u8)));
assert_eq!(d3.first_nonzero_nibble(), Some((1, 0x4u8)));
assert_eq!(d4.first_nonzero_nibble(), Some((0, 0x9u8)));
// Verify bits
assert_eq!(d1.bit(0), true);
assert_eq!(d1.bit(1), false);
assert_eq!(d1.bit(7), false);
assert_eq!(d1.bit(8), false);
assert_eq!(d1.bit(14), true);
assert_eq!(d1.bit(15), false);
assert_eq!(d1.bit(254), true);
assert_eq!(d1.bit(255), false);
assert_eq!(d1.first_nonzero_bit(), Some(0));
assert_eq!(d2.first_nonzero_bit(), Some(0));
assert_eq!(d3.first_nonzero_bit(), Some(5));
assert_eq!(d4.first_nonzero_bit(), Some(0));
}
pub async fn test_all() {
let api = crypto_tests_startup().await;
let crypto = api.crypto().unwrap();
// Test versions
for v in VALID_CRYPTO_KINDS {
let vcrypto = crypto.get(v).unwrap();
test_generate_secret(vcrypto.clone()).await;
test_sign_and_verify(vcrypto.clone()).await;
test_key_conversions(vcrypto.clone()).await;
test_encode_decode(vcrypto.clone()).await;
test_hash(vcrypto.clone()).await;
test_operations(vcrypto).await;
}
crypto_tests_shutdown(api.clone()).await;
assert!(api.is_shutdown());
}

View File

@ -0,0 +1,184 @@
use super::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
pub struct CryptoTyped<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ Ord
+ PartialOrd
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
pub kind: CryptoKind,
pub value: K,
}
impl<K> CryptoTyped<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ Ord
+ PartialOrd
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
pub fn new(kind: CryptoKind, value: K) -> Self {
Self { kind, value }
}
}
impl<K> PartialOrd for CryptoTyped<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ Ord
+ PartialOrd
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<K> Ord for CryptoTyped<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ Ord
+ PartialOrd
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
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<K> fmt::Display for CryptoTyped<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ Ord
+ PartialOrd
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}:{}", self.kind, self.value)
}
}
impl<K> FromStr for CryptoTyped<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ Ord
+ PartialOrd
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
type Err = VeilidAPIError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let b = s.as_bytes();
if b.len() != (5 + K::encoded_len()) || b[4..5] != b":"[..] {
apibail_parse_error!("invalid typed key", s);
}
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 })
}
}
impl<'de, K> Deserialize<'de> for CryptoTyped<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ Ord
+ PartialOrd
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <String as Deserialize>::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(serde::de::Error::custom)
}
}
impl<K> Serialize for CryptoTyped<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ Ord
+ PartialOrd
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_str(self)
}
}

View File

@ -0,0 +1,303 @@
use super::*;
#[derive(
Clone,
Debug,
Serialize,
Deserialize,
PartialOrd,
Ord,
PartialEq,
Eq,
Hash,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
Default,
)]
#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
#[serde(from = "Vec<CryptoTyped<K>>", into = "Vec<CryptoTyped<K>>")]
pub struct CryptoTypedSet<K = PublicKey>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
<Vec<CryptoTyped<K>> as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
items: Vec<CryptoTyped<K>>,
}
impl<K> CryptoTypedSet<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
pub fn new() -> Self {
Self { items: Vec::new() }
}
pub fn with_capacity(cap: usize) -> Self {
Self {
items: Vec::with_capacity(cap),
}
}
pub fn kinds(&self) -> Vec<CryptoKind> {
let mut out = Vec::new();
for tk in &self.items {
out.push(tk.kind);
}
out.sort_by(compare_crypto_kind);
out
}
pub fn keys(&self) -> Vec<K> {
let mut out = Vec::new();
for tk in &self.items {
out.push(tk.value);
}
out
}
pub fn get(&self, kind: CryptoKind) -> Option<CryptoTyped<K>> {
self.items.iter().find(|x| x.kind == kind).copied()
}
pub fn add(&mut self, typed_key: CryptoTyped<K>) {
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: &[CryptoTyped<K>]) {
'outer: for typed_key in typed_keys {
for x in &mut self.items {
if x.kind == typed_key.kind {
*x = *typed_key;
continue 'outer;
}
}
self.items.push(*typed_key);
}
self.items.sort()
}
pub fn remove(&mut self, kind: CryptoKind) {
if let Some(idx) = self.items.iter().position(|x| x.kind == kind) {
self.items.remove(idx);
}
}
pub fn remove_all(&mut self, kinds: &[CryptoKind]) {
for k in kinds {
self.remove(*k);
}
}
/// Return preferred typed key of our supported crypto kinds
pub fn best(&self) -> Option<CryptoTyped<K>> {
match self.items.first().copied() {
None => None,
Some(k) => {
if !VALID_CRYPTO_KINDS.contains(&k.kind) {
None
} else {
Some(k)
}
}
}
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn iter(&self) -> core::slice::Iter<'_, CryptoTyped<K>> {
self.items.iter()
}
pub fn contains(&self, typed_key: &CryptoTyped<K>) -> bool {
self.items.contains(typed_key)
}
pub fn contains_any(&self, typed_keys: &[CryptoTyped<K>]) -> bool {
for typed_key in typed_keys {
if self.items.contains(typed_key) {
return true;
}
}
false
}
pub fn contains_key(&self, key: &K) -> bool {
for tk in &self.items {
if tk.value == *key {
return true;
}
}
false
}
}
impl<K> core::ops::Deref for CryptoTypedSet<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
type Target = [CryptoTyped<K>];
#[inline]
fn deref(&self) -> &[CryptoTyped<K>] {
&self.items
}
}
impl<K> fmt::Display for CryptoTypedSet<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "[")?;
let mut first = true;
for x in &self.items {
if !first {
write!(f, ",")?;
first = false;
}
write!(f, "{}", x)?;
}
write!(f, "]")
}
}
impl<K> FromStr for CryptoTypedSet<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
type Err = VeilidAPIError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
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::<K>::from_str(x.trim())?;
items.push(tk);
}
Ok(Self { items })
}
}
impl<K> From<CryptoTyped<K>> for CryptoTypedSet<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn from(x: CryptoTyped<K>) -> Self {
let mut tks = CryptoTypedSet::<K>::with_capacity(1);
tks.add(x);
tks
}
}
impl<K> From<Vec<CryptoTyped<K>>> for CryptoTypedSet<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn from(x: Vec<CryptoTyped<K>>) -> Self {
let mut tks = CryptoTypedSet::<K>::with_capacity(x.len());
tks.add_all(&x);
tks
}
}
impl<K> Into<Vec<CryptoTyped<K>>> for CryptoTypedSet<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn into(self) -> Vec<CryptoTyped<K>> {
self.items
}
}

View File

@ -0,0 +1,91 @@
use super::*;
#[derive(
Clone,
Copy,
Serialize,
Deserialize,
PartialOrd,
Ord,
PartialEq,
Eq,
Hash,
RkyvArchive,
RkyvSerialize,
RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes, Hash, PartialEq, Eq))]
pub struct KeyPair {
pub key: PublicKey,
pub secret: SecretKey,
}
impl KeyPair {
pub fn new(key: PublicKey, secret: SecretKey) -> Self {
Self { key, secret }
}
pub fn split(&self) -> (PublicKey, SecretKey) {
(self.key, self.secret)
}
pub fn into_split(self) -> (PublicKey, SecretKey) {
(self.key, self.secret)
}
}
impl Encodable for KeyPair {
fn encode(&self) -> String {
format!("{}:{}", self.key.encode(), self.secret.encode())
}
fn encoded_len() -> usize {
PublicKey::encoded_len() + 1 + SecretKey::encoded_len()
}
fn try_decode_bytes(b: &[u8]) -> Result<Self, VeilidAPIError> {
if b.len() != Self::encoded_len() {
apibail_parse_error!("input has wrong encoded length", format!("len={}", b.len()));
}
let key = PublicKey::try_decode_bytes(&b[0..PublicKey::encoded_len()])?;
let secret = SecretKey::try_decode_bytes(&b[(PublicKey::encoded_len() + 1)..])?;
Ok(KeyPair { key, secret })
}
}
impl fmt::Display for KeyPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.encode())
}
}
impl fmt::Debug for KeyPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($name), "("))?;
write!(f, "{}", self.encode())?;
write!(f, ")")
}
}
impl From<&KeyPair> for String {
fn from(value: &KeyPair) -> Self {
value.encode()
}
}
impl FromStr for KeyPair {
type Err = VeilidAPIError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
KeyPair::try_from(s)
}
}
impl TryFrom<String> for KeyPair {
type Error = VeilidAPIError;
fn try_from(value: String) -> Result<Self, Self::Error> {
KeyPair::try_from(value.as_str())
}
}
impl TryFrom<&str> for KeyPair {
type Error = VeilidAPIError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::try_decode(value)
}
}

View File

@ -0,0 +1,59 @@
use super::*;
use core::cmp::{Eq, Ord, PartialEq, PartialOrd};
use core::convert::TryInto;
use core::fmt;
use core::hash::Hash;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
/// Cryptography version fourcc code
pub type CryptoKind = FourCC;
/// Sort best crypto kinds first
/// Better crypto kinds are 'less', ordered toward the front of a list
pub fn compare_crypto_kind(a: &CryptoKind, b: &CryptoKind) -> cmp::Ordering {
let a_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == a);
let b_idx = VALID_CRYPTO_KINDS.iter().position(|k| k == b);
if let Some(a_idx) = a_idx {
if let Some(b_idx) = b_idx {
// Both are valid, prefer better crypto kind
a_idx.cmp(&b_idx)
} else {
// A is valid, B is not
cmp::Ordering::Less
}
} else if b_idx.is_some() {
// B is valid, A is not
cmp::Ordering::Greater
} else {
// Both are invalid, so use lex comparison
a.cmp(b)
}
}
/// Intersection of crypto kind vectors
pub fn common_crypto_kinds(a: &[CryptoKind], b: &[CryptoKind]) -> Vec<CryptoKind> {
let mut out = Vec::new();
for ack in a {
if b.contains(ack) {
out.push(*ack);
}
}
out
}
mod crypto_typed;
mod crypto_typed_set;
mod keypair;
pub use crypto_typed::*;
pub use crypto_typed_set::*;
pub use keypair::*;
pub type TypedKey = CryptoTyped<PublicKey>;
pub type TypedSecret = CryptoTyped<SecretKey>;
pub type TypedKeyPair = CryptoTyped<KeyPair>;
pub type TypedSignature = CryptoTyped<Signature>;
pub type TypedKeySet = CryptoTypedSet<PublicKey>;
pub type TypedSecretSet = CryptoTypedSet<SecretKey>;

View File

@ -0,0 +1,70 @@
use digest::generic_array::typenum::U64;
use digest::{Digest, Output};
use generic_array::GenericArray;
pub struct Blake3Digest512 {
dig: blake3::Hasher,
}
impl Digest for Blake3Digest512 {
type OutputSize = U64;
fn new() -> Self {
Self {
dig: blake3::Hasher::new(),
}
}
fn update(&mut self, data: impl AsRef<[u8]>) {
self.dig.update(data.as_ref());
}
fn chain(mut self, data: impl AsRef<[u8]>) -> Self
where
Self: Sized,
{
self.update(data);
self
}
fn finalize(self) -> Output<Self> {
let mut b = [0u8; 64];
self.dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
out
}
fn finalize_reset(&mut self) -> Output<Self> {
let mut b = [0u8; 64];
self.dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
self.reset();
out
}
fn reset(&mut self) {
self.dig.reset();
}
fn output_size() -> usize {
64
}
fn digest(data: &[u8]) -> Output<Self> {
let mut dig = blake3::Hasher::new();
dig.update(data);
let mut b = [0u8; 64];
dig.finalize_xof().fill(&mut b);
let mut out = GenericArray::<u8, U64>::default();
for n in 0..64 {
out[n] = b[n];
}
out
}
}

View File

@ -0,0 +1,300 @@
pub mod blake3digest512;
pub use blake3digest512::*;
use super::*;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use chacha20poly1305 as ch;
use chacha20poly1305::aead::{AeadInPlace, NewAead};
use core::convert::TryInto;
use curve25519_dalek as cd;
use digest::Digest;
use ed25519_dalek as ed;
use x25519_dalek as xd;
const AEAD_OVERHEAD: usize = 16;
pub const CRYPTO_KIND_VLD0: CryptoKind = FourCC([b'V', b'L', b'D', b'0']);
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, VeilidAPIError> {
let bytes = key.to_bytes();
let compressed = cd::edwards::CompressedEdwardsY(bytes);
let point = compressed
.decompress()
.ok_or_else(|| VeilidAPIError::internal("ed25519_to_x25519_pk failed"))?;
let mp = point.to_montgomery();
Ok(xd::PublicKey::from(mp.to_bytes()))
}
fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result<xd::StaticSecret, VeilidAPIError> {
let exp = ed::ExpandedSecretKey::from(key);
let bytes: [u8; ed::EXPANDED_SECRET_KEY_LENGTH] = exp.to_bytes();
let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(VeilidAPIError::internal)?;
Ok(xd::StaticSecret::from(lowbytes))
}
pub fn vld0_generate_keypair() -> KeyPair {
let mut csprng = VeilidRng {};
let keypair = ed::Keypair::generate(&mut csprng);
let dht_key = PublicKey::new(keypair.public.to_bytes());
let dht_key_secret = SecretKey::new(keypair.secret.to_bytes());
KeyPair::new(dht_key, dht_key_secret)
}
/// V0 CryptoSystem
#[derive(Clone)]
pub struct CryptoSystemVLD0 {
crypto: Crypto,
}
impl CryptoSystemVLD0 {
pub fn new(crypto: Crypto) -> Self {
Self { crypto }
}
}
impl CryptoSystem for CryptoSystemVLD0 {
// Accessors
fn kind(&self) -> CryptoKind {
CRYPTO_KIND_VLD0
}
fn crypto(&self) -> Crypto {
self.crypto.clone()
}
// Cached Operations
fn cached_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
self.crypto
.cached_dh_internal::<CryptoSystemVLD0>(self, key, secret)
}
// Generation
fn random_nonce(&self) -> Nonce {
let mut nonce = [0u8; 24];
random_bytes(&mut nonce).unwrap();
Nonce::new(nonce)
}
fn random_shared_secret(&self) -> SharedSecret {
let mut s = [0u8; 32];
random_bytes(&mut s).unwrap();
SharedSecret::new(s)
}
fn compute_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(VeilidAPIError::internal)?;
let pk_xd = ed25519_to_x25519_pk(&pk_ed)?;
let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(VeilidAPIError::internal)?;
let sk_xd = ed25519_to_x25519_sk(&sk_ed)?;
Ok(SharedSecret::new(sk_xd.diffie_hellman(&pk_xd).to_bytes()))
}
fn generate_keypair(&self) -> KeyPair {
vld0_generate_keypair()
}
fn generate_hash(&self, data: &[u8]) -> PublicKey {
PublicKey::new(*blake3::hash(data).as_bytes())
}
fn generate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
) -> Result<PublicKey, VeilidAPIError> {
let mut hasher = blake3::Hasher::new();
std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
Ok(PublicKey::new(*hasher.finalize().as_bytes()))
}
// Validation
fn validate_keypair(&self, dht_key: &PublicKey, dht_key_secret: &SecretKey) -> bool {
let data = vec![0u8; 512];
let sig = match self.sign(dht_key, dht_key_secret, &data) {
Ok(s) => s,
Err(_) => {
return false;
}
};
self.verify(dht_key, &data, &sig).is_ok()
}
fn validate_hash(&self, data: &[u8], dht_key: &PublicKey) -> bool {
let bytes = *blake3::hash(data).as_bytes();
bytes == dht_key.bytes
}
fn validate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
dht_key: &PublicKey,
) -> Result<bool, VeilidAPIError> {
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: &PublicKey, key2: &PublicKey) -> PublicKeyDistance {
let mut bytes = [0u8; PUBLIC_KEY_LENGTH];
for (n, byte) in bytes.iter_mut().enumerate() {
*byte = key1.bytes[n] ^ key2.bytes[n];
}
PublicKeyDistance::new(bytes)
}
// Authentication
fn sign(
&self,
dht_key: &PublicKey,
dht_key_secret: &SecretKey,
data: &[u8],
) -> Result<Signature, VeilidAPIError> {
let mut kpb: [u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH] =
[0u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH];
kpb[..SECRET_KEY_LENGTH].copy_from_slice(&dht_key_secret.bytes);
kpb[SECRET_KEY_LENGTH..].copy_from_slice(&dht_key.bytes);
let keypair = ed::Keypair::from_bytes(&kpb)
.map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?;
let mut dig = Blake3Digest512::new();
dig.update(data);
let sig = keypair
.sign_prehashed(dig, None)
.map_err(VeilidAPIError::internal)?;
let dht_sig = Signature::new(sig.to_bytes());
Ok(dht_sig)
}
fn verify(
&self,
dht_key: &PublicKey,
data: &[u8],
signature: &Signature,
) -> Result<(), VeilidAPIError> {
let pk = ed::PublicKey::from_bytes(&dht_key.bytes)
.map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?;
let sig = ed::Signature::from_bytes(&signature.bytes)
.map_err(|e| VeilidAPIError::parse_error("Signature is invalid", e))?;
let mut dig = Blake3Digest512::new();
dig.update(data);
pk.verify_prehashed(dig, None, &sig)
.map_err(|e| VeilidAPIError::parse_error("Verification failed", e))?;
Ok(())
}
// AEAD Encrypt/Decrypt
fn aead_overhead(&self) -> usize {
AEAD_OVERHEAD
}
fn decrypt_in_place_aead(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
let key = ch::Key::from(shared_secret.bytes);
let xnonce = ch::XNonce::from(nonce.bytes);
let aead = ch::XChaCha20Poly1305::new(&key);
aead.decrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)
}
fn decrypt_aead(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
let mut out = body.to_vec();
self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)?;
Ok(out)
}
fn encrypt_in_place_aead(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
let key = ch::Key::from(shared_secret.bytes);
let xnonce = ch::XNonce::from(nonce.bytes);
let aead = ch::XChaCha20Poly1305::new(&key);
aead.encrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)
}
fn encrypt_aead(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
let mut out = body.to_vec();
self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
.map_err(VeilidAPIError::generic)?;
Ok(out)
}
// NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
) {
let mut cipher = XChaCha20::new(&shared_secret.bytes.into(), &nonce.bytes.into());
cipher.apply_keystream(body);
}
fn crypt_b2b_no_auth(
&self,
in_buf: &[u8],
out_buf: &mut [u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) {
let mut cipher = XChaCha20::new(&shared_secret.bytes.into(), &nonce.bytes.into());
cipher.apply_keystream_b2b(in_buf, out_buf).unwrap();
}
fn crypt_no_auth_aligned_8(
&self,
in_buf: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) -> Vec<u8> {
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);
out_buf
}
fn crypt_no_auth_unaligned(
&self,
in_buf: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
) -> Vec<u8> {
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);
out_buf
}
}

View File

@ -113,28 +113,35 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret_string(&self, key: &str, value: &str) -> EyreResult<bool> {
pub async fn save_user_secret_string<K: AsRef<str> + fmt::Debug, V: AsRef<str> + fmt::Debug>(
&self,
key: K,
value: V,
) -> EyreResult<bool> {
let inner = self.inner.lock();
inner
.keyring_manager
.as_ref()
.ok_or_else(|| eyre!("Protected store not initialized"))?
.with_keyring(&self.service_name(), key, |kr| {
.with_keyring(&self.service_name(), key.as_ref(), |kr| {
let existed = kr.get_value().is_ok();
kr.set_value(value)?;
kr.set_value(value.as_ref())?;
Ok(existed)
})
.wrap_err("failed to save user secret")
}
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret_string(&self, key: &str) -> EyreResult<Option<String>> {
pub async fn load_user_secret_string<K: AsRef<str> + fmt::Debug>(
&self,
key: K,
) -> EyreResult<Option<String>> {
let inner = self.inner.lock();
match inner
.keyring_manager
.as_ref()
.ok_or_else(|| eyre!("Protected store not initialized"))?
.with_keyring(&self.service_name(), key, |kr| kr.get_value())
.with_keyring(&self.service_name(), key.as_ref(), |kr| kr.get_value())
{
Ok(v) => Ok(Some(v)),
Err(KeyringError::NoPasswordFound) => Ok(None),
@ -143,17 +150,19 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self, value))]
pub async fn save_user_secret_rkyv<T>(&self, key: &str, value: &T) -> EyreResult<bool>
pub async fn save_user_secret_rkyv<K, T>(&self, key: K, value: &T) -> EyreResult<bool>
where
K: AsRef<str> + fmt::Debug,
T: RkyvSerialize<rkyv::ser::serializers::AllocSerializer<1024>>,
{
let v = to_rkyv(value)?;
self.save_user_secret(&key, &v).await
self.save_user_secret(key, &v).await
}
#[instrument(level = "trace", skip(self, value))]
pub async fn save_user_secret_json<T>(&self, key: &str, value: &T) -> EyreResult<bool>
pub async fn save_user_secret_json<K, T>(&self, key: K, value: &T) -> EyreResult<bool>
where
K: AsRef<str> + fmt::Debug,
T: serde::Serialize,
{
let v = serde_json::to_vec(value)?;
@ -161,8 +170,9 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self))]
pub async fn load_user_secret_rkyv<T>(&self, key: &str) -> EyreResult<Option<T>>
pub async fn load_user_secret_rkyv<K, T>(&self, key: K) -> EyreResult<Option<T>>
where
K: AsRef<str> + fmt::Debug,
T: RkyvArchive,
<T as RkyvArchive>::Archived:
for<'t> bytecheck::CheckBytes<rkyv::validation::validators::DefaultValidator<'t>>,
@ -182,8 +192,9 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self))]
pub async fn load_user_secret_json<T>(&self, key: &str) -> EyreResult<Option<T>>
pub async fn load_user_secret_json<K, T>(&self, key: K) -> EyreResult<Option<T>>
where
K: AsRef<str> + fmt::Debug,
T: for<'de> serde::de::Deserialize<'de>,
{
let out = self.load_user_secret(key).await?;
@ -199,7 +210,11 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret(&self, key: &str, value: &[u8]) -> EyreResult<bool> {
pub async fn save_user_secret<K: AsRef<str> + fmt::Debug>(
&self,
key: K,
value: &[u8],
) -> EyreResult<bool> {
let mut s = BASE64URL_NOPAD.encode(value);
s.push('!');
@ -207,7 +222,10 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret(&self, key: &str) -> EyreResult<Option<Vec<u8>>> {
pub async fn load_user_secret<K: AsRef<str> + fmt::Debug>(
&self,
key: K,
) -> EyreResult<Option<Vec<u8>>> {
let mut s = match self.load_user_secret_string(key).await? {
Some(s) => s,
None => {
@ -238,13 +256,13 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> {
pub async fn remove_user_secret<K: AsRef<str> + fmt::Debug>(&self, key: K) -> EyreResult<bool> {
let inner = self.inner.lock();
match inner
.keyring_manager
.as_ref()
.ok_or_else(|| eyre!("Protected store not initialized"))?
.with_keyring(&self.service_name(), key, |kr| kr.delete_value())
.with_keyring(&self.service_name(), key.as_ref(), |kr| kr.delete_value())
{
Ok(_) => Ok(true),
Err(KeyringError::NoPasswordFound) => Ok(false),

View File

@ -50,7 +50,11 @@ impl ProtectedStore {
}
//#[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret_string(&self, key: &str, value: &str) -> EyreResult<bool> {
pub async fn save_user_secret_string<K: AsRef<str> + fmt::Debug, V: AsRef<str> + fmt::Debug>(
&self,
key: K,
value: V,
) -> EyreResult<bool> {
if is_browser() {
let win = match window() {
Some(w) => w,
@ -70,7 +74,7 @@ impl ProtectedStore {
}
};
let vkey = self.browser_key_name(key);
let vkey = self.browser_key_name(key.as_ref());
let prev = match ls
.get_item(&vkey)
@ -81,7 +85,7 @@ impl ProtectedStore {
None => false,
};
ls.set_item(&vkey, value)
ls.set_item(&vkey, value.as_ref())
.map_err(map_jsvalue_error)
.wrap_err("exception_thrown")?;
@ -92,7 +96,10 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret_string(&self, key: &str) -> EyreResult<Option<String>> {
pub async fn load_user_secret_string<K: AsRef<str> + fmt::Debug>(
&self,
key: K,
) -> EyreResult<Option<String>> {
if is_browser() {
let win = match window() {
Some(w) => w,
@ -112,7 +119,7 @@ impl ProtectedStore {
}
};
let vkey = self.browser_key_name(key);
let vkey = self.browser_key_name(key.as_ref());
ls.get_item(&vkey)
.map_err(map_jsvalue_error)
@ -123,26 +130,29 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self, value))]
pub async fn save_user_secret_rkyv<T>(&self, key: &str, value: &T) -> EyreResult<bool>
pub async fn save_user_secret_rkyv<K, T>(&self, key: K, value: &T) -> EyreResult<bool>
where
K: AsRef<str> + fmt::Debug,
T: RkyvSerialize<rkyv::ser::serializers::AllocSerializer<1024>>,
{
let v = to_rkyv(value)?;
self.save_user_secret(&key, &v).await
self.save_user_secret(key, &v).await
}
#[instrument(level = "trace", skip(self, value))]
pub async fn save_user_secret_json<T>(&self, key: &str, value: &T) -> EyreResult<bool>
pub async fn save_user_secret_json<K, T>(&self, key: K, value: &T) -> EyreResult<bool>
where
K: AsRef<str> + fmt::Debug,
T: serde::Serialize,
{
let v = serde_json::to_vec(value)?;
self.save_user_secret(&key, &v).await
self.save_user_secret(key, &v).await
}
#[instrument(level = "trace", skip(self))]
pub async fn load_user_secret_rkyv<T>(&self, key: &str) -> EyreResult<Option<T>>
pub async fn load_user_secret_rkyv<K, T>(&self, key: K) -> EyreResult<Option<T>>
where
K: AsRef<str> + fmt::Debug,
T: RkyvArchive,
<T as RkyvArchive>::Archived:
for<'t> bytecheck::CheckBytes<rkyv::validation::validators::DefaultValidator<'t>>,
@ -162,8 +172,9 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self))]
pub async fn load_user_secret_json<T>(&self, key: &str) -> EyreResult<Option<T>>
pub async fn load_user_secret_json<K, T>(&self, key: K) -> EyreResult<Option<T>>
where
K: AsRef<str> + fmt::Debug,
T: for<'de> serde::de::Deserialize<'de>,
{
let out = self.load_user_secret(key).await?;
@ -179,7 +190,11 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self, value), ret, err)]
pub async fn save_user_secret(&self, key: &str, value: &[u8]) -> EyreResult<bool> {
pub async fn save_user_secret<K: AsRef<str> + fmt::Debug>(
&self,
key: K,
value: &[u8],
) -> EyreResult<bool> {
let mut s = BASE64URL_NOPAD.encode(value);
s.push('!');
@ -187,7 +202,10 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self), err)]
pub async fn load_user_secret(&self, key: &str) -> EyreResult<Option<Vec<u8>>> {
pub async fn load_user_secret<K: AsRef<str> + fmt::Debug>(
&self,
key: K,
) -> EyreResult<Option<Vec<u8>>> {
let mut s = match self.load_user_secret_string(key).await? {
Some(s) => s,
None => {
@ -218,7 +236,7 @@ impl ProtectedStore {
}
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn remove_user_secret(&self, key: &str) -> EyreResult<bool> {
pub async fn remove_user_secret<K: AsRef<str> + fmt::Debug>(&self, key: K) -> EyreResult<bool> {
if is_browser() {
let win = match window() {
Some(w) => w,
@ -238,7 +256,7 @@ impl ProtectedStore {
}
};
let vkey = self.browser_key_name(key);
let vkey = self.browser_key_name(key.as_ref());
match ls
.get_item(&vkey)

View File

@ -35,6 +35,7 @@ mod veilid_layer_filter;
pub use self::api_tracing_layer::ApiTracingLayer;
pub use self::core_context::{api_startup, api_startup_json, UpdateCallback};
pub use self::crypto::vld0_generate_keypair;
pub use self::veilid_api::*;
pub use self::veilid_config::*;
pub use self::veilid_layer_filter::*;

View File

@ -83,6 +83,7 @@ impl ConnectionTable {
unord.push(v);
}
}
inner.protocol_index_by_id.clear();
inner.id_by_descriptor.clear();
inner.ids_by_remote.clear();
unord
@ -136,7 +137,9 @@ impl ConnectionTable {
};
// Add the connection to the table
let res = inner.conn_by_id[protocol_index].insert(id, network_connection);
let res = inner.conn_by_id[protocol_index].insert(id, network_connection, |_k, _v| {
// never lrus, unbounded
});
assert!(res.is_none());
// if we have reached the maximum number of connections per protocol type

View File

@ -120,7 +120,7 @@ pub(crate) enum NodeContactMethod {
Direct(DialInfo),
/// Request via signal the node connect back directly (relay, target)
SignalReverse(NodeRef, NodeRef),
/// Request via signal the node negotiate a hole punch (relay, target_node)
/// Request via signal the node negotiate a hole punch (relay, target)
SignalHolePunch(NodeRef, NodeRef),
/// Must use an inbound relay to reach the node
InboundRelay(NodeRef),
@ -134,7 +134,7 @@ struct PublicAddressCheckCacheKey(ProtocolType, AddressType);
// The mutable state of the network manager
struct NetworkManagerInner {
stats: NetworkManagerStats,
client_whitelist: LruCache<DHTKey, ClientWhitelistEntry>,
client_whitelist: LruCache<TypedKey, ClientWhitelistEntry>,
public_address_check_cache:
BTreeMap<PublicAddressCheckCacheKey, LruCache<IpAddr, SocketAddress>>,
public_address_inconsistencies_table:
@ -396,9 +396,11 @@ impl NetworkManager {
debug!("finished network manager shutdown");
}
pub fn update_client_whitelist(&self, client: DHTKey) {
pub fn update_client_whitelist(&self, client: TypedKey) {
let mut inner = self.inner.lock();
match inner.client_whitelist.entry(client) {
match inner.client_whitelist.entry(client, |_k,_v| {
// do nothing on LRU evict
}) {
hashlink::lru_cache::Entry::Occupied(mut entry) => {
entry.get_mut().last_seen_ts = get_aligned_timestamp()
}
@ -411,10 +413,12 @@ impl NetworkManager {
}
#[instrument(level = "trace", skip(self), ret)]
pub fn check_client_whitelist(&self, client: DHTKey) -> bool {
pub fn check_client_whitelist(&self, client: TypedKey) -> bool {
let mut inner = self.inner.lock();
match inner.client_whitelist.entry(client) {
match inner.client_whitelist.entry(client, |_k,_v| {
// do nothing on LRU evict
}) {
hashlink::lru_cache::Entry::Occupied(mut entry) => {
entry.get_mut().last_seen_ts = get_aligned_timestamp();
true
@ -519,10 +523,15 @@ impl NetworkManager {
let routing_table = self.routing_table();
// Generate receipt and serialized form to return
let nonce = Crypto::get_random_nonce();
let receipt = Receipt::try_new(0, nonce, routing_table.node_id(), extra_data)?;
let vcrypto = self.crypto().best();
let nonce = vcrypto.random_nonce();
let node_id = routing_table.node_id(vcrypto.kind());
let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind());
let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?;
let out = receipt
.to_signed_data(&routing_table.node_id_secret())
.to_signed_data(self.crypto(), &node_id_secret)
.wrap_err("failed to generate signed receipt")?;
// Record the receipt for later
@ -543,10 +552,15 @@ impl NetworkManager {
let routing_table = self.routing_table();
// Generate receipt and serialized form to return
let nonce = Crypto::get_random_nonce();
let receipt = Receipt::try_new(0, nonce, routing_table.node_id(), extra_data)?;
let vcrypto = self.crypto().best();
let nonce = vcrypto.random_nonce();
let node_id = routing_table.node_id(vcrypto.kind());
let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind());
let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?;
let out = receipt
.to_signed_data(&routing_table.node_id_secret())
.to_signed_data(self.crypto(), &node_id_secret)
.wrap_err("failed to generate signed receipt")?;
// Record the receipt for later
@ -566,7 +580,7 @@ impl NetworkManager {
) -> NetworkResult<()> {
let receipt_manager = self.receipt_manager();
let receipt = match Receipt::from_signed_data(receipt_data.as_ref()) {
let receipt = match Receipt::from_signed_data(self.crypto(), receipt_data.as_ref()) {
Err(e) => {
return NetworkResult::invalid_message(e.to_string());
}
@ -587,7 +601,7 @@ impl NetworkManager {
) -> NetworkResult<()> {
let receipt_manager = self.receipt_manager();
let receipt = match Receipt::from_signed_data(receipt_data.as_ref()) {
let receipt = match Receipt::from_signed_data(self.crypto(), receipt_data.as_ref()) {
Err(e) => {
return NetworkResult::invalid_message(e.to_string());
}
@ -607,7 +621,7 @@ impl NetworkManager {
) -> NetworkResult<()> {
let receipt_manager = self.receipt_manager();
let receipt = match Receipt::from_signed_data(receipt_data.as_ref()) {
let receipt = match Receipt::from_signed_data(self.crypto(), receipt_data.as_ref()) {
Err(e) => {
return NetworkResult::invalid_message(e.to_string());
}
@ -624,11 +638,11 @@ impl NetworkManager {
pub async fn handle_private_receipt<R: AsRef<[u8]>>(
&self,
receipt_data: R,
private_route: DHTKey,
private_route: PublicKey,
) -> NetworkResult<()> {
let receipt_manager = self.receipt_manager();
let receipt = match Receipt::from_signed_data(receipt_data.as_ref()) {
let receipt = match Receipt::from_signed_data(self.crypto(), receipt_data.as_ref()) {
Err(e) => {
return NetworkResult::invalid_message(e.to_string());
}
@ -649,10 +663,9 @@ impl NetworkManager {
let rpc = self.rpc_processor();
// Add the peer info to our routing table
let peer_nr = match routing_table.register_node_with_signed_node_info(
let peer_nr = match routing_table.register_node_with_peer_info(
RoutingDomain::PublicInternet,
peer_info.node_id.key,
peer_info.signed_node_info,
peer_info,
false,
) {
None => {
@ -673,10 +686,9 @@ impl NetworkManager {
let rpc = self.rpc_processor();
// Add the peer info to our routing table
let mut peer_nr = match routing_table.register_node_with_signed_node_info(
let mut peer_nr = match routing_table.register_node_with_peer_info(
RoutingDomain::PublicInternet,
peer_info.node_id.key,
peer_info.signed_node_info,
peer_info,
false,
) {
None => {
@ -731,21 +743,25 @@ impl NetworkManager {
#[instrument(level = "trace", skip(self, body), err)]
fn build_envelope<B: AsRef<[u8]>>(
&self,
dest_node_id: DHTKey,
dest_node_id: TypedKey,
version: u8,
body: B,
) -> EyreResult<Vec<u8>> {
// DH to get encryption key
let routing_table = self.routing_table();
let node_id = routing_table.node_id();
let node_id_secret = routing_table.node_id_secret();
let Some(vcrypto) = self.crypto().get(dest_node_id.kind) else {
bail!("should not have a destination with incompatible crypto here");
};
let node_id = routing_table.node_id(vcrypto.kind());
let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind());
// Get timestamp, nonce
let ts = get_aligned_timestamp();
let nonce = Crypto::get_random_nonce();
let nonce = vcrypto.random_nonce();
// Encode envelope
let envelope = Envelope::new(version, ts, nonce, node_id, dest_node_id);
let envelope = Envelope::new(version, node_id.kind, ts, nonce, node_id.value, dest_node_id.value);
envelope
.to_encrypted_data(self.crypto(), body.as_ref(), &node_id_secret)
.wrap_err("envelope failed to encode")
@ -753,50 +769,44 @@ impl NetworkManager {
/// Called by the RPC handler when we want to issue an RPC request or response
/// node_ref is the direct destination to which the envelope will be sent
/// If 'node_id' is specified, it can be different than node_ref.node_id()
/// If 'destination_node_ref' is specified, it can be different than the node_ref being sent to
/// which will cause the envelope to be relayed
#[instrument(level = "trace", skip(self, body), ret, err)]
pub async fn send_envelope<B: AsRef<[u8]>>(
&self,
node_ref: NodeRef,
envelope_node_id: Option<DHTKey>,
destination_node_ref: Option<NodeRef>,
body: B,
) -> EyreResult<NetworkResult<SendDataKind>> {
let via_node_id = node_ref.node_id();
let envelope_node_id = envelope_node_id.unwrap_or(via_node_id);
if envelope_node_id != via_node_id {
let destination_node_ref = destination_node_ref.as_ref().unwrap_or(&node_ref).clone();
if !node_ref.same_entry(&destination_node_ref) {
log_net!(
"sending envelope to {:?} via {:?}",
envelope_node_id,
destination_node_ref,
node_ref
);
} else {
log_net!("sending envelope to {:?}", node_ref);
}
// Get node's min/max version and see if we can send to it
let best_node_id = destination_node_ref.best_node_id();
// Get node's envelope versions and see if we can send to it
// and if so, get the max version we can use
let version = if let Some(min_max_version) = node_ref.min_max_version() {
#[allow(clippy::absurd_extreme_comparisons)]
if min_max_version.min > MAX_CRYPTO_VERSION || min_max_version.max < MIN_CRYPTO_VERSION
{
bail!(
"can't talk to this node {} because version is unsupported: ({},{})",
via_node_id,
min_max_version.min,
min_max_version.max
);
}
cmp::min(min_max_version.max, MAX_CRYPTO_VERSION)
} else {
MAX_CRYPTO_VERSION
let Some(envelope_version) = destination_node_ref.best_envelope_version() else {
bail!(
"can't talk to this node {} because we dont support its envelope versions",
node_ref
);
};
// Build the envelope to send
let out = self.build_envelope(envelope_node_id, version, body)?;
let out = self.build_envelope(best_node_id, envelope_version, body)?;
// Send the envelope via whatever means necessary
self.send_data(node_ref.clone(), out).await
self.send_data(node_ref, out).await
}
/// Called by the RPC handler when we want to issue an direct receipt
@ -860,7 +870,7 @@ impl NetworkManager {
let rpc = self.rpc_processor();
network_result_try!(rpc
.rpc_call_signal(
Destination::relay(relay_nr, target_nr.node_id()),
Destination::relay(relay_nr, target_nr.clone()),
SignalInfo::ReverseConnect { receipt, peer_info },
)
.await
@ -886,7 +896,7 @@ impl NetworkManager {
// We expect the inbound noderef to be the same as the target noderef
// if they aren't the same, we should error on this and figure out what then hell is up
if target_nr.node_id() != inbound_nr.node_id() {
if !target_nr.same_entry(&inbound_nr) {
bail!("unexpected noderef mismatch on reverse connect");
}
@ -965,7 +975,7 @@ impl NetworkManager {
let rpc = self.rpc_processor();
network_result_try!(rpc
.rpc_call_signal(
Destination::relay(relay_nr, target_nr.node_id()),
Destination::relay(relay_nr, target_nr.clone()),
SignalInfo::HolePunch { receipt, peer_info },
)
.await
@ -991,7 +1001,7 @@ impl NetworkManager {
// We expect the inbound noderef to be the same as the target noderef
// if they aren't the same, we should error on this and figure out what then hell is up
if target_nr.node_id() != inbound_nr.node_id() {
if !target_nr.same_entry(&inbound_nr) {
bail!(
"unexpected noderef mismatch on hole punch {}, expected {}",
inbound_nr,
@ -1069,7 +1079,7 @@ impl NetworkManager {
let relay_nr = routing_table
.lookup_and_filter_noderef(relay_key, routing_domain.into(), dial_info_filter)
.ok_or_else(|| eyre!("couldn't look up relay"))?;
if target_node_ref.node_id() != target_key {
if !target_node_ref.node_ids().contains(&target_key) {
bail!("target noderef didn't match target key");
}
NodeContactMethod::SignalReverse(relay_nr, target_node_ref)
@ -1078,7 +1088,7 @@ impl NetworkManager {
let relay_nr = routing_table
.lookup_and_filter_noderef(relay_key, routing_domain.into(), dial_info_filter)
.ok_or_else(|| eyre!("couldn't look up relay"))?;
if target_node_ref.node_id() != target_key {
if target_node_ref.node_ids().contains(&target_key) {
bail!("target noderef didn't match target key");
}
NodeContactMethod::SignalHolePunch(relay_nr, target_node_ref)
@ -1320,13 +1330,13 @@ impl NetworkManager {
}
// Is this an out-of-band receipt instead of an envelope?
if data[0..4] == *RECEIPT_MAGIC {
if data[0..3] == *RECEIPT_MAGIC {
network_result_value_or_log!(self.handle_out_of_band_receipt(data).await => {});
return Ok(true);
}
// Decode envelope header (may fail signature validation)
let envelope = match Envelope::from_signed_data(data) {
let envelope = match Envelope::from_signed_data(self.crypto(), data) {
Ok(v) => v,
Err(e) => {
log_net!(debug "envelope failed to decode: {}", e);
@ -1370,16 +1380,16 @@ impl NetworkManager {
// Peek at header and see if we need to relay this
// If the recipient id is not our node id, then it needs relaying
let sender_id = envelope.get_sender_id();
let recipient_id = envelope.get_recipient_id();
if recipient_id != routing_table.node_id() {
let sender_id = TypedKey::new(envelope.get_crypto_kind(), envelope.get_sender_id());
let recipient_id = TypedKey::new(envelope.get_crypto_kind(), envelope.get_recipient_id());
if !routing_table.matches_own_node_id(&[recipient_id]) {
// See if the source node is allowed to resolve nodes
// This is a costly operation, so only outbound-relay permitted
// nodes are allowed to do this, for example PWA users
let some_relay_nr = if self.check_client_whitelist(sender_id) {
// Full relay allowed, do a full resolve_node
match rpc.resolve_node(recipient_id).await {
match rpc.resolve_node(recipient_id.value).await {
Ok(v) => v,
Err(e) => {
log_net!(debug "failed to resolve recipient node for relay, dropping outbound relayed packet: {}" ,e);
@ -1417,7 +1427,7 @@ impl NetworkManager {
}
// DH to get decryption key (cached)
let node_id_secret = routing_table.node_id_secret();
let node_id_secret = routing_table.node_id_secret_key(envelope.get_crypto_kind());
// Decrypt the envelope body
let body = match envelope
@ -1432,7 +1442,7 @@ impl NetworkManager {
// Cache the envelope information in the routing table
let source_noderef = match routing_table.register_node_with_existing_connection(
envelope.get_sender_id(),
TypedKey::new(envelope.get_crypto_kind(), envelope.get_sender_id()),
connection_descriptor,
ts,
) {
@ -1443,7 +1453,7 @@ impl NetworkManager {
}
Some(v) => v,
};
source_noderef.set_min_max_version(envelope.get_min_max_version());
source_noderef.add_envelope_version(envelope.get_version());
// xxx: deal with spoofing and flooding here?
@ -1471,7 +1481,9 @@ impl NetworkManager {
inner
.stats
.per_address_stats
.entry(PerAddressStatsKey(addr))
.entry(PerAddressStatsKey(addr), |_k,_v| {
// do nothing on LRU evict
})
.or_insert(PerAddressStats::default())
.transfer_stats_accounting
.add_up(bytes);
@ -1487,7 +1499,9 @@ impl NetworkManager {
inner
.stats
.per_address_stats
.entry(PerAddressStatsKey(addr))
.entry(PerAddressStatsKey(addr), |_k,_v| {
// do nothing on LRU evict
})
.or_insert(PerAddressStats::default())
.transfer_stats_accounting
.add_down(bytes);
@ -1537,7 +1551,7 @@ impl NetworkManager {
if let Some(nr) = routing_table.lookup_node_ref(k) {
let peer_stats = nr.peer_stats();
let peer = PeerTableData {
node_id: k,
node_ids: nr.node_ids(),
peer_address: v.last_connection.remote(),
peer_stats,
};
@ -1622,7 +1636,9 @@ impl NetworkManager {
if pait.contains_key(&ipblock) {
return;
}
pacc.insert(ipblock, socket_address);
pacc.insert(ipblock, socket_address, |_k,_v| {
// do nothing on LRU evict
});
// Determine if our external address has likely changed
let mut bad_public_address_detection_punishment: Option<

View File

@ -112,7 +112,7 @@ impl DiscoveryContext {
&self,
protocol_type: ProtocolType,
address_type: AddressType,
ignore_node: Option<DHTKey>,
ignore_node_ids: Option<TypedKeySet>,
) -> Option<(SocketAddress, NodeRef)> {
let node_count = {
let config = self.routing_table.network_manager().config();
@ -121,7 +121,7 @@ impl DiscoveryContext {
};
// Build an filter that matches our protocol and address type
// and excludes relays so we can get an accurate external address
// and excludes relayed nodes so we can get an accurate external address
let dial_info_filter = DialInfoFilter::all()
.with_protocol_type(protocol_type)
.with_address_type(address_type);
@ -130,11 +130,11 @@ impl DiscoveryContext {
dial_info_filter.clone(),
);
let disallow_relays_filter = Box::new(
move |rti: &RoutingTableInner, _k: DHTKey, v: Option<Arc<BucketEntry>>| {
move |rti: &RoutingTableInner, v: Option<Arc<BucketEntry>>| {
let v = v.unwrap();
v.with(rti, |_rti, e| {
if let Some(n) = e.signed_node_info(RoutingDomain::PublicInternet) {
n.relay_id().is_none()
n.relay_ids().is_empty()
} else {
false
}
@ -158,8 +158,8 @@ impl DiscoveryContext {
// For each peer, if it's not our ignore-node, ask them for our public address, filtering on desired dial info
for mut peer in peers {
if let Some(ignore_node) = ignore_node {
if peer.node_id() == ignore_node {
if let Some(ignore_node_ids) = &ignore_node_ids {
if peer.node_ids().contains_any(ignore_node_ids) {
continue;
}
}
@ -478,12 +478,12 @@ impl DiscoveryContext {
// Get our external address from some fast node, that is not node 1, call it node 2
let (external_2_address, node_2) = match self
.discover_external_address(protocol_type, address_type, Some(node_1.node_id()))
.discover_external_address(protocol_type, address_type, Some(node_1.node_ids()))
.await
{
None => {
// If we can't get an external address, allow retry
log_net!(debug "failed to discover external address 2 for {:?}:{:?}, skipping node {:?}", protocol_type, address_type, node_1.node_id());
log_net!(debug "failed to discover external address 2 for {:?}:{:?}, skipping node {:?}", protocol_type, address_type, node_1);
return Ok(false);
}
Some(v) => v,

View File

@ -11,7 +11,7 @@ pub enum ReceiptEvent {
ReturnedOutOfBand,
ReturnedInBand { inbound_noderef: NodeRef },
ReturnedSafety,
ReturnedPrivate { private_route: DHTKey },
ReturnedPrivate { private_route: PublicKey },
Expired,
Cancelled,
}
@ -21,7 +21,7 @@ pub enum ReceiptReturned {
OutOfBand,
InBand { inbound_noderef: NodeRef },
Safety,
Private { private_route: DHTKey },
Private { private_route: PublicKey },
}
pub trait ReceiptCallback: Send + 'static {
@ -149,7 +149,7 @@ impl PartialOrd for ReceiptRecordTimestampSort {
pub struct ReceiptManagerInner {
network_manager: NetworkManager,
records_by_nonce: BTreeMap<ReceiptNonce, Arc<Mutex<ReceiptRecord>>>,
records_by_nonce: BTreeMap<Nonce, Arc<Mutex<ReceiptRecord>>>,
next_oldest_ts: Option<Timestamp>,
stop_source: Option<StopSource>,
timeout_task: MustJoinSingleFuture<()>,
@ -370,7 +370,7 @@ impl ReceiptManager {
inner.next_oldest_ts = new_next_oldest_ts;
}
pub async fn cancel_receipt(&self, nonce: &ReceiptNonce) -> EyreResult<()> {
pub async fn cancel_receipt(&self, nonce: &Nonce) -> EyreResult<()> {
log_rpc!(debug "== Cancel Receipt {}", nonce.encode());
// Remove the record

View File

@ -2,25 +2,31 @@ use super::*;
use core::sync::atomic::Ordering;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
/// 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
/// 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 {
routing_table: RoutingTable,
entries: BTreeMap<DHTKey, Arc<BucketEntry>>,
newest_entry: Option<DHTKey>,
/// Map of keys to entries for this bucket
entries: BTreeMap<PublicKey, Arc<BucketEntry>>,
/// The crypto kind in use for the public keys in this bucket
kind: CryptoKind,
}
pub(super) type EntriesIter<'a> = alloc::collections::btree_map::Iter<'a, DHTKey, Arc<BucketEntry>>;
pub(super) type EntriesIter<'a> =
alloc::collections::btree_map::Iter<'a, PublicKey, Arc<BucketEntry>>;
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
struct BucketEntryData {
key: DHTKey,
value: Vec<u8>,
struct SerializedBucketEntryData {
key: PublicKey,
value: u32, // index into serialized entries list
}
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
struct BucketData {
entries: Vec<BucketEntryData>,
newest_entry: Option<DHTKey>,
struct SerializedBucketData {
entries: Vec<SerializedBucketEntryData>,
}
fn state_ordering(state: BucketEntryState) -> usize {
@ -32,68 +38,79 @@ fn state_ordering(state: BucketEntryState) -> usize {
}
impl Bucket {
pub fn new(routing_table: RoutingTable) -> Self {
pub fn new(kind: CryptoKind) -> Self {
Self {
routing_table,
entries: BTreeMap::new(),
newest_entry: None,
kind,
}
}
pub(super) fn load_bucket(&mut self, data: Vec<u8>) -> EyreResult<()> {
let bucket_data: BucketData = from_rkyv(data)?;
pub(super) fn load_bucket(
&mut self,
data: Vec<u8>,
all_entries: &[Arc<BucketEntry>],
) -> EyreResult<()> {
let bucket_data: SerializedBucketData = from_rkyv(data)?;
for e in bucket_data.entries {
let entryinner = from_rkyv(e.value).wrap_err("failed to deserialize bucket entry")?;
self.entries
.insert(e.key, Arc::new(BucketEntry::new_with_inner(entryinner)));
.insert(e.key, all_entries[e.value as usize].clone());
}
self.newest_entry = bucket_data.newest_entry;
Ok(())
}
pub(super) fn save_bucket(&self) -> EyreResult<Vec<u8>> {
pub(super) fn save_bucket(
&self,
all_entries: &mut Vec<Arc<BucketEntry>>,
entry_map: &mut HashMap<*const BucketEntry, u32>,
) -> EyreResult<Vec<u8>> {
let mut entries = Vec::new();
for (k, v) in &self.entries {
let entry_bytes = v.with_inner(|e| to_rkyv(e))?;
entries.push(BucketEntryData {
let entry_index = entry_map.entry(Arc::as_ptr(v)).or_insert_with(|| {
let entry_index = all_entries.len();
all_entries.push(v.clone());
entry_index as u32
});
entries.push(SerializedBucketEntryData {
key: *k,
value: entry_bytes,
value: *entry_index,
});
}
let bucket_data = BucketData {
entries,
newest_entry: self.newest_entry.clone(),
};
let bucket_data = SerializedBucketData { entries };
let out = to_rkyv(&bucket_data)?;
Ok(out)
}
pub(super) fn add_entry(&mut self, node_id: DHTKey) -> NodeRef {
log_rtab!("Node added: {}", node_id.encode());
/// Create a new entry with a node_id of this crypto kind and return it
pub(super) fn add_new_entry(&mut self, node_id_key: PublicKey) -> Arc<BucketEntry> {
log_rtab!("Node added: {}:{}", self.kind, node_id_key);
// Add new entry
self.entries.insert(node_id, Arc::new(BucketEntry::new()));
let entry = Arc::new(BucketEntry::new(TypedKey::new(self.kind, node_id_key)));
self.entries.insert(node_id_key, entry.clone());
// This is now the newest bucket entry
self.newest_entry = Some(node_id);
// Get a node ref to return
let entry = self.entries.get(&node_id).unwrap().clone();
NodeRef::new(self.routing_table.clone(), node_id, entry, None)
// Return the new entry
entry
}
pub(super) fn remove_entry(&mut self, node_id: &DHTKey) {
log_rtab!("Node removed: {}", node_id);
/// Add an existing entry with a new node_id for this crypto kind
pub(super) fn add_existing_entry(&mut self, node_id_key: PublicKey, entry: Arc<BucketEntry>) {
log_rtab!("Existing node added: {}:{}", self.kind, node_id_key);
// Add existing entry
self.entries.insert(node_id_key, entry);
}
/// Remove an entry with a node_id for this crypto kind from the bucket
pub(super) fn remove_entry(&mut self, node_id_key: &PublicKey) {
log_rtab!("Node removed: {}:{}", self.kind, node_id_key);
// Remove the entry
self.entries.remove(node_id);
// newest_entry is updated by kick_bucket()
self.entries.remove(node_id_key);
}
pub(super) fn entry(&self, key: &DHTKey) -> Option<Arc<BucketEntry>> {
pub(super) fn entry(&self, key: &PublicKey) -> Option<Arc<BucketEntry>> {
self.entries.get(key).cloned()
}
@ -101,7 +118,7 @@ impl Bucket {
self.entries.iter()
}
pub(super) fn kick(&mut self, bucket_depth: usize) -> Option<BTreeSet<DHTKey>> {
pub(super) fn kick(&mut self, bucket_depth: usize) -> Option<BTreeSet<PublicKey>> {
// Get number of entries to attempt to purge from bucket
let bucket_len = self.entries.len();
@ -111,11 +128,11 @@ impl Bucket {
}
// Try to purge the newest entries that overflow the bucket
let mut dead_node_ids: BTreeSet<DHTKey> = BTreeSet::new();
let mut dead_node_ids: BTreeSet<PublicKey> = BTreeSet::new();
let mut extra_entries = bucket_len - bucket_depth;
// Get the sorted list of entries by their kick order
let mut sorted_entries: Vec<(DHTKey, Arc<BucketEntry>)> = self
let mut sorted_entries: Vec<(PublicKey, Arc<BucketEntry>)> = self
.entries
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
@ -144,24 +161,15 @@ impl Bucket {
})
});
self.newest_entry = None;
for entry in sorted_entries {
// If we're not evicting more entries, exit, noting this may be the newest entry
if extra_entries == 0 {
// The first 'live' entry we find is our newest entry
if self.newest_entry.is_none() {
self.newest_entry = Some(entry.0);
}
break;
}
extra_entries -= 1;
// if this entry has references we can't drop it yet
if entry.1.ref_count.load(Ordering::Acquire) > 0 {
// The first 'live' entry we fine is our newest entry
if self.newest_entry.is_none() {
self.newest_entry = Some(entry.0);
}
continue;
}

View File

@ -68,23 +68,14 @@ pub struct BucketEntryLocalNetwork {
node_status: Option<LocalNetworkNodeStatus>,
}
/// A range of cryptography versions supported by this entry
#[derive(Copy, Clone, Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct VersionRange {
/// The minimum cryptography version supported by this entry
pub min: u8,
/// The maximum cryptography version supported by this entry
pub max: u8,
}
/// The data associated with each bucket entry
#[derive(Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct BucketEntryInner {
/// The minimum and maximum range of cryptography versions supported by the node,
/// inclusive of the requirements of any relay the node may be using
min_max_version: Option<VersionRange>,
/// The node ids matching this bucket entry, with the cryptography versions supported by this node as the 'kind' field
node_ids: TypedKeySet,
/// The set of envelope versions supported by the node inclusive of the requirements of any relay the node may be using
envelope_support: Vec<u8>,
/// If this node has updated it's SignedNodeInfo since our network
/// and dial info has last changed, for example when our IP address changes
/// Used to determine if we should make this entry 'live' again when we receive a signednodeinfo update that
@ -131,6 +122,38 @@ impl BucketEntryInner {
self.node_ref_tracks.remove(&track_id);
}
/// Get node ids
pub fn node_ids(&self) -> TypedKeySet {
self.node_ids.clone()
}
/// Add a node id for a particular crypto kind.
/// Returns any previous existing node id associated with that crypto kind
pub fn add_node_id(&mut self, node_id: TypedKey) -> Option<TypedKey> {
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 None;
}
self.node_ids.add(node_id);
return Some(old_node_id);
}
self.node_ids.add(node_id);
None
}
pub fn best_node_id(&self) -> TypedKey {
self.node_ids.best().unwrap()
}
/// Get crypto kinds
pub fn crypto_kinds(&self) -> Vec<CryptoKind> {
self.node_ids.kinds()
}
/// Compare sets of crypto kinds
pub fn common_crypto_kinds(&self, other: &[CryptoKind]) -> Vec<CryptoKind> {
common_crypto_kinds(&self.node_ids.kinds(), other)
}
// Less is faster
pub fn cmp_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering {
// Lower latency to the front
@ -219,7 +242,7 @@ impl BucketEntryInner {
// See if we have an existing signed_node_info to update or not
if let Some(current_sni) = opt_current_sni {
// Always allow overwriting invalid/unsigned node
if current_sni.has_valid_signature() {
if current_sni.has_any_signature() {
// If the timestamp hasn't changed or is less, ignore this update
if signed_node_info.timestamp() <= current_sni.timestamp() {
// If we received a node update with the same timestamp
@ -238,25 +261,12 @@ impl BucketEntryInner {
}
}
// Update the protocol min/max version we have to use, to include relay requirements if needed
let mut version_range = VersionRange {
min: signed_node_info.node_info().min_version,
max: signed_node_info.node_info().max_version,
};
if let Some(relay_info) = signed_node_info.relay_info() {
version_range.min.max_assign(relay_info.min_version);
version_range.max.min_assign(relay_info.max_version);
}
if version_range.min <= version_range.max {
// Can be reached with at least one crypto version
self.min_max_version = Some(version_range);
} else {
// No valid crypto version in range
self.min_max_version = None;
}
// Update the envelope version support we have to use
let envelope_support = signed_node_info.node_info().envelope_support.clone();
// Update the signed node info
*opt_current_sni = Some(Box::new(signed_node_info));
self.set_envelope_support(envelope_support);
self.updated_since_last_network_change = true;
self.touch_last_seen(get_aligned_timestamp());
}
@ -310,13 +320,13 @@ impl BucketEntryInner {
opt_current_sni.as_ref().map(|s| s.as_ref())
}
pub fn make_peer_info(&self, key: DHTKey, routing_domain: RoutingDomain) -> Option<PeerInfo> {
pub fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
let opt_current_sni = match routing_domain {
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
};
opt_current_sni.as_ref().map(|s| PeerInfo {
node_id: NodeId::new(key),
node_ids: self.node_ids.clone(),
signed_node_info: *s.clone(),
})
}
@ -447,12 +457,27 @@ impl BucketEntryInner {
out
}
pub fn set_min_max_version(&mut self, min_max_version: VersionRange) {
self.min_max_version = Some(min_max_version);
pub fn add_envelope_version(&mut self, envelope_version: u8) {
if self.envelope_support.contains(&envelope_version) {
return;
}
self.envelope_support.push(envelope_version);
self.envelope_support.dedup();
self.envelope_support.sort();
}
pub fn min_max_version(&self) -> Option<VersionRange> {
self.min_max_version
pub fn set_envelope_support(&mut self, mut envelope_support: Vec<u8>) {
envelope_support.dedup();
envelope_support.sort();
self.envelope_support = envelope_support;
}
pub fn envelope_support(&self) -> Vec<u8> {
self.envelope_support.clone()
}
pub fn best_envelope_version(&self) -> Option<u8> {
self.envelope_support.iter().rev().find(|x| VALID_ENVELOPE_VERSIONS.contains(x)).copied()
}
pub fn state(&self, cur_ts: Timestamp) -> BucketEntryState {
@ -746,38 +771,41 @@ pub struct BucketEntry {
}
impl BucketEntry {
pub(super) fn new() -> Self {
pub(super) fn new(first_node_id: TypedKey) -> Self {
let now = get_aligned_timestamp();
Self {
ref_count: AtomicU32::new(0),
inner: RwLock::new(BucketEntryInner {
min_max_version: None,
updated_since_last_network_change: false,
last_connections: BTreeMap::new(),
local_network: BucketEntryLocalNetwork {
last_seen_our_node_info_ts: Timestamp::new(0u64),
signed_node_info: None,
node_status: None,
},
public_internet: BucketEntryPublicInternet {
last_seen_our_node_info_ts: Timestamp::new(0u64),
signed_node_info: None,
node_status: None,
},
peer_stats: PeerStats {
time_added: now,
rpc_stats: RPCStats::default(),
latency: None,
transfer: TransferStatsDownUp::default(),
},
latency_stats_accounting: LatencyStatsAccounting::new(),
transfer_stats_accounting: TransferStatsAccounting::new(),
#[cfg(feature = "tracking")]
next_track_id: 0,
#[cfg(feature = "tracking")]
node_ref_tracks: HashMap::new(),
}),
}
let mut node_ids = TypedKeySet::new();
node_ids.add(first_node_id);
let inner = BucketEntryInner {
node_ids,
envelope_support: Vec::new(),
updated_since_last_network_change: false,
last_connections: BTreeMap::new(),
local_network: BucketEntryLocalNetwork {
last_seen_our_node_info_ts: Timestamp::new(0u64),
signed_node_info: None,
node_status: None,
},
public_internet: BucketEntryPublicInternet {
last_seen_our_node_info_ts: Timestamp::new(0u64),
signed_node_info: None,
node_status: None,
},
peer_stats: PeerStats {
time_added: now,
rpc_stats: RPCStats::default(),
latency: None,
transfer: TransferStatsDownUp::default(),
},
latency_stats_accounting: LatencyStatsAccounting::new(),
transfer_stats_accounting: TransferStatsAccounting::new(),
#[cfg(feature = "tracking")]
next_track_id: 0,
#[cfg(feature = "tracking")]
node_ref_tracks: HashMap::new(),
};
Self::new_with_inner(inner)
}
pub(super) fn new_with_inner(inner: BucketEntryInner) -> Self {

View File

@ -1,5 +1,5 @@
use super::*;
use routing_table::tasks::bootstrap::BOOTSTRAP_TXT_VERSION;
use routing_table::tasks::bootstrap::BOOTSTRAP_TXT_VERSION_0;
impl RoutingTable {
pub(crate) fn debug_info_nodeinfo(&self) -> String {
@ -7,7 +7,7 @@ impl RoutingTable {
let inner = self.inner.read();
out += "Routing Table Info:\n";
out += &format!(" Node Id: {}\n", self.unlocked_inner.node_id.encode());
out += &format!(" Node Ids: {}\n", self.unlocked_inner.node_ids());
out += &format!(
" Self Latency Stats Accounting: {:#?}\n\n",
inner.self_latency_stats_accounting
@ -55,13 +55,20 @@ impl RoutingTable {
short_urls.sort();
short_urls.dedup();
let valid_envelope_versions = VALID_ENVELOPE_VERSIONS.map(|x| x.to_string()).join(",");
let node_ids = self
.unlocked_inner
.node_ids()
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(",");
out += "TXT Record:\n";
out += &format!(
"{},{},{},{},{}",
BOOTSTRAP_TXT_VERSION,
MIN_CRYPTO_VERSION,
MAX_CRYPTO_VERSION,
self.node_id().encode(),
"{}|{}|{}|{}|",
BOOTSTRAP_TXT_VERSION_0,
valid_envelope_versions,
node_ids,
some_hostname.unwrap()
);
for short_url in short_urls {
@ -108,56 +115,53 @@ impl RoutingTable {
let mut out = String::new();
let blen = inner.buckets.len();
let mut b = 0;
let mut cnt = 0;
out += &format!("Entries: {}\n", inner.bucket_entry_count);
while b < blen {
let filtered_entries: Vec<(&DHTKey, &Arc<BucketEntry>)> = inner.buckets[b]
.entries()
.filter(|e| {
let state = e.1.with(inner, |_rti, e| e.state(cur_ts));
state >= min_state
})
.collect();
if !filtered_entries.is_empty() {
out += &format!(" Bucket #{}:\n", b);
for e in filtered_entries {
let state = e.1.with(inner, |_rti, e| e.state(cur_ts));
out += &format!(
" {} [{}]\n",
e.0.encode(),
match state {
BucketEntryState::Reliable => "R",
BucketEntryState::Unreliable => "U",
BucketEntryState::Dead => "D",
}
);
out += &format!("Entries: {}\n", inner.bucket_entry_count());
cnt += 1;
for ck in &VALID_CRYPTO_KINDS {
let blen = inner.buckets[ck].len();
while b < blen {
let filtered_entries: Vec<(&PublicKey, &Arc<BucketEntry>)> = inner.buckets[ck][b]
.entries()
.filter(|e| {
let state = e.1.with(inner, |_rti, e| e.state(cur_ts));
state >= min_state
})
.collect();
if !filtered_entries.is_empty() {
out += &format!("{} Bucket #{}:\n", ck, b);
for e in filtered_entries {
let state = e.1.with(inner, |_rti, e| e.state(cur_ts));
out += &format!(
" {} [{}]\n",
e.0.encode(),
match state {
BucketEntryState::Reliable => "R",
BucketEntryState::Unreliable => "U",
BucketEntryState::Dead => "D",
}
);
cnt += 1;
if cnt >= limit {
break;
}
}
if cnt >= limit {
break;
}
}
if cnt >= limit {
break;
}
b += 1;
}
b += 1;
}
out
}
pub(crate) fn debug_info_entry(&self, node_id: DHTKey) -> String {
pub(crate) fn debug_info_entry(&self, node_ref: NodeRef) -> String {
let mut out = String::new();
out += &format!("Entry {:?}:\n", node_id);
if let Some(nr) = self.lookup_node_ref(node_id) {
out += &nr.operate(|_rt, e| format!("{:#?}\n", e));
} else {
out += "Entry not found\n";
}
out += &node_ref.operate(|_rt, e| format!("{:#?}\n", e));
out
}
@ -168,26 +172,28 @@ impl RoutingTable {
let mut out = String::new();
const COLS: usize = 16;
let rows = inner.buckets.len() / COLS;
let mut r = 0;
let mut b = 0;
out += "Buckets:\n";
while r < rows {
let mut c = 0;
out += format!(" {:>3}: ", b).as_str();
while c < COLS {
let mut cnt = 0;
for e in inner.buckets[b].entries() {
if e.1.with(inner, |_rti, e| e.state(cur_ts) >= min_state) {
cnt += 1;
for ck in &VALID_CRYPTO_KINDS {
let rows = inner.buckets[ck].len() / COLS;
let mut r = 0;
let mut b = 0;
while r < rows {
let mut c = 0;
out += format!(" {:>3}: ", b).as_str();
while c < COLS {
let mut cnt = 0;
for e in inner.buckets[ck][b].entries() {
if e.1.with(inner, |_rti, e| e.state(cur_ts) >= min_state) {
cnt += 1;
}
}
out += format!("{:>3} ", cnt).as_str();
b += 1;
c += 1;
}
out += format!("{:>3} ", cnt).as_str();
b += 1;
c += 1;
out += "\n";
r += 1;
}
out += "\n";
r += 1;
}
out

View File

@ -49,7 +49,7 @@ pub struct LowLevelPortInfo {
pub protocol_to_port: ProtocolToPortMapping,
}
pub type RoutingTableEntryFilter<'t> =
Box<dyn FnMut(&RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> bool + Send + 't>;
Box<dyn FnMut(&RoutingTableInner, Option<Arc<BucketEntry>>) -> bool + Send + 't>;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct RoutingTableHealth {
@ -65,17 +65,19 @@ pub struct RoutingTableHealth {
pub local_network_ready: bool,
}
pub(super) struct RoutingTableUnlockedInner {
pub type BucketIndex = (CryptoKind, usize);
pub struct RoutingTableUnlockedInner {
// Accessors
config: VeilidConfig,
network_manager: NetworkManager,
/// The current node's public DHT key
node_id: DHTKey,
/// The current node's DHT key secret
node_id_secret: DHTKeySecret,
/// The current node's public DHT keys
node_id: TypedKeySet,
/// The current node's public DHT secrets
node_id_secret: TypedSecretSet,
/// Buckets to kick on our next kick task
kick_queue: Mutex<BTreeSet<usize>>,
kick_queue: Mutex<BTreeSet<BucketIndex>>,
/// Background process for computing statistics
rolling_transfers_task: TickTask<EyreReport>,
/// Background process to purge dead routing table entries when necessary
@ -92,6 +94,83 @@ pub(super) struct RoutingTableUnlockedInner {
private_route_management_task: TickTask<EyreReport>,
}
impl RoutingTableUnlockedInner {
pub fn network_manager(&self) -> NetworkManager {
self.network_manager.clone()
}
pub fn crypto(&self) -> Crypto {
self.network_manager().crypto()
}
pub fn rpc_processor(&self) -> RPCProcessor {
self.network_manager().rpc_processor()
}
pub fn update_callback(&self) -> UpdateCallback {
self.network_manager().update_callback()
}
pub fn with_config<F, R>(&self, f: F) -> R
where
F: FnOnce(&VeilidConfigInner) -> R,
{
f(&*self.config.get())
}
pub fn node_id(&self, kind: CryptoKind) -> TypedKey {
self.node_id.get(kind).unwrap()
}
pub fn node_id_secret_key(&self, kind: CryptoKind) -> SecretKey {
self.node_id_secret.get(kind).unwrap().value
}
pub fn node_ids(&self) -> TypedKeySet {
self.node_id.clone()
}
pub fn node_id_typed_key_pairs(&self) -> Vec<TypedKeyPair> {
let mut tkps = Vec::new();
for ck in VALID_CRYPTO_KINDS {
tkps.push(TypedKeyPair::new(
ck,
KeyPair::new(self.node_id(ck).value, self.node_id_secret_key(ck)),
));
}
tkps
}
pub fn matches_own_node_id(&self, node_ids: &[TypedKey]) -> bool {
for ni in node_ids {
if let Some(v) = self.node_id.get(ni.kind) {
if v.value == ni.value {
return true;
}
}
}
false
}
pub fn matches_own_node_id_key(&self, node_id_key: &PublicKey) -> bool {
for tk in self.node_id.iter() {
if tk.value == *node_id_key {
return true;
}
}
false
}
pub fn calculate_bucket_index(&self, node_id: &TypedKey) -> BucketIndex {
let crypto = self.crypto();
let self_node_id_key = self.node_id(node_id.kind).value;
let vcrypto = crypto.get(node_id.kind).unwrap();
(
node_id.kind,
vcrypto
.distance(&node_id.value, &self_node_id_key)
.first_nonzero_bit()
.unwrap(),
)
}
}
#[derive(Clone)]
pub struct RoutingTable {
inner: Arc<RwLock<RoutingTableInner>>,
@ -104,11 +183,12 @@ impl RoutingTable {
network_manager: NetworkManager,
) -> RoutingTableUnlockedInner {
let c = config.get();
RoutingTableUnlockedInner {
config: config.clone(),
network_manager,
node_id: c.network.node_id.unwrap(),
node_id_secret: c.network.node_id_secret.unwrap(),
node_id: c.network.routing_table.node_id.clone(),
node_id_secret: c.network.routing_table.node_id_secret.clone(),
kick_queue: Mutex::new(BTreeSet::default()),
rolling_transfers_task: TickTask::new(ROLLING_TRANSFERS_INTERVAL_SECS),
kick_buckets_task: TickTask::new(1),
@ -133,30 +213,6 @@ impl RoutingTable {
this
}
pub fn network_manager(&self) -> NetworkManager {
self.unlocked_inner.network_manager.clone()
}
pub fn rpc_processor(&self) -> RPCProcessor {
self.network_manager().rpc_processor()
}
pub fn update_callback(&self) -> UpdateCallback {
self.network_manager().update_callback()
}
pub fn with_config<F, R>(&self, f: F) -> R
where
F: FnOnce(&VeilidConfigInner) -> R,
{
f(&*self.unlocked_inner.config.get())
}
pub fn node_id(&self) -> DHTKey {
self.unlocked_inner.node_id
}
pub fn node_id_secret(&self) -> DHTKeySecret {
self.unlocked_inner.node_id_secret
}
/////////////////////////////////////
/// Initialization
@ -167,7 +223,7 @@ impl RoutingTable {
// Set up routing buckets
{
let mut inner = self.inner.write();
inner.init_buckets(self.clone());
inner.init_buckets();
}
// Load bucket entries from table db if possible
@ -175,7 +231,7 @@ impl RoutingTable {
if let Err(e) = self.load_buckets().await {
log_rtab!(debug "Error loading buckets from storage: {:#?}. Resetting.", e);
let mut inner = self.inner.write();
inner.init_buckets(self.clone());
inner.init_buckets();
}
// Set up routespecstore
@ -229,56 +285,99 @@ impl RoutingTable {
debug!("finished routing table terminate");
}
/// Serialize routing table to table store
async fn save_buckets(&self) -> EyreResult<()> {
// Serialize all entries
let mut bucketvec: Vec<Vec<u8>> = Vec::new();
// Since entries are shared by multiple buckets per cryptokind
// we need to get the list of all unique entries when serializing
let mut all_entries: Vec<Arc<BucketEntry>> = Vec::new();
// Serialize all buckets and get map of entries
let mut serialized_bucket_map: BTreeMap<CryptoKind, Vec<Vec<u8>>> = BTreeMap::new();
{
let mut entry_map: HashMap<*const BucketEntry, u32> = HashMap::new();
let inner = &*self.inner.read();
for bucket in &inner.buckets {
bucketvec.push(bucket.save_bucket()?)
for ck in VALID_CRYPTO_KINDS {
let buckets = inner.buckets.get(&ck).unwrap();
let mut serialized_buckets = Vec::new();
for bucket in buckets.iter() {
serialized_buckets.push(bucket.save_bucket(&mut all_entries, &mut entry_map)?)
}
serialized_bucket_map.insert(ck, serialized_buckets);
}
}
let table_store = self.network_manager().table_store();
// Serialize all the entries
let mut all_entry_bytes = Vec::with_capacity(all_entries.len());
for entry in all_entries {
// Serialize entry
let entry_bytes = entry.with_inner(|e| to_rkyv(e))?;
all_entry_bytes.push(entry_bytes);
}
let table_store = self.unlocked_inner.network_manager().table_store();
let tdb = table_store.open("routing_table", 1).await?;
let bucket_count = bucketvec.len();
let dbx = tdb.transact();
if let Err(e) = dbx.store_rkyv(0, b"bucket_count", &bucket_count) {
if let Err(e) = dbx.store_rkyv(0, b"serialized_bucket_map", &serialized_bucket_map) {
dbx.rollback();
return Err(e);
}
for (n, b) in bucketvec.iter().enumerate() {
dbx.store(0, format!("bucket_{}", n).as_bytes(), b)
if let Err(e) = dbx.store_rkyv(0, b"all_entry_bytes", &all_entry_bytes) {
dbx.rollback();
return Err(e);
}
dbx.commit().await?;
Ok(())
}
/// Deserialize routing table from table store
async fn load_buckets(&self) -> EyreResult<()> {
// Deserialize all entries
let tstore = self.network_manager().table_store();
// Deserialize bucket map and all entries from the table store
let tstore = self.unlocked_inner.network_manager().table_store();
let tdb = tstore.open("routing_table", 1).await?;
let Some(bucket_count): Option<usize> = tdb.load_rkyv(0, b"bucket_count")? else {
log_rtab!(debug "no bucket count in saved routing table");
let Some(serialized_bucket_map): Option<BTreeMap<CryptoKind, Vec<Vec<u8>>>> = tdb.load_rkyv(0, b"serialized_bucket_map")? else {
log_rtab!(debug "no bucket map in saved routing table");
return Ok(());
};
let inner = &mut *self.inner.write();
if bucket_count != inner.buckets.len() {
// Must have the same number of buckets
warn!("bucket count is different, not loading routing table");
let Some(all_entry_bytes): Option<Vec<Vec<u8>>> = tdb.load_rkyv(0, b"all_entry_bytes")? else {
log_rtab!(debug "no all_entry_bytes in saved routing table");
return Ok(());
};
// Reconstruct all entries
let inner = &mut *self.inner.write();
let mut all_entries: Vec<Arc<BucketEntry>> = Vec::with_capacity(all_entry_bytes.len());
for entry_bytes in all_entry_bytes {
let entryinner =
from_rkyv(entry_bytes).wrap_err("failed to deserialize bucket entry")?;
let entry = Arc::new(BucketEntry::new_with_inner(entryinner));
// Keep strong reference in table
all_entries.push(entry.clone());
// Keep all entries in weak table too
inner.all_entries.insert(entry);
}
let mut bucketdata_vec: Vec<Vec<u8>> = Vec::new();
for n in 0..bucket_count {
let Some(bucketdata): Option<Vec<u8>> =
tdb.load(0, format!("bucket_{}", n).as_bytes())? else {
warn!("bucket data not loading, skipping loading routing table");
return Ok(());
};
bucketdata_vec.push(bucketdata);
// Validate serialized bucket map
for (k, v) in &serialized_bucket_map {
if !VALID_CRYPTO_KINDS.contains(k) {
warn!("crypto kind is not valid, not loading routing table");
return Ok(());
}
if v.len() != PUBLIC_KEY_LENGTH * 8 {
warn!("bucket count is different, not loading routing table");
return Ok(());
}
}
for (n, bucketdata) in bucketdata_vec.into_iter().enumerate() {
inner.buckets[n].load_bucket(bucketdata)?;
// Recreate buckets
for (k, v) in serialized_bucket_map {
let buckets = inner.buckets.get_mut(&k).unwrap();
for n in 0..v.len() {
buckets[n].load_bucket(v[n].clone(), &all_entries)?;
}
}
Ok(())
@ -443,7 +542,7 @@ impl RoutingTable {
}
/// Attempt to empty the routing table
/// should only be performed when there are no node_refs (detached)
/// May not empty buckets completely if there are existing node_refs
pub fn purge_buckets(&self) {
self.inner.write().purge_buckets();
}
@ -453,20 +552,25 @@ impl RoutingTable {
self.inner.write().purge_last_connections();
}
fn find_bucket_index(&self, node_id: DHTKey) -> usize {
distance(&node_id, &self.unlocked_inner.node_id)
.first_nonzero_bit()
.unwrap()
}
pub fn get_entry_count(
&self,
routing_domain_set: RoutingDomainSet,
min_state: BucketEntryState,
crypto_kinds: &[CryptoKind],
) -> usize {
self.inner
.read()
.get_entry_count(routing_domain_set, min_state)
.get_entry_count(routing_domain_set, min_state, crypto_kinds)
}
pub fn get_entry_count_per_crypto_kind(
&self,
routing_domain_set: RoutingDomainSet,
min_state: BucketEntryState,
) -> BTreeMap<CryptoKind, usize> {
self.inner
.read()
.get_entry_count_per_crypto_kind(routing_domain_set, min_state)
}
pub fn get_nodes_needing_ping(
@ -484,32 +588,29 @@ impl RoutingTable {
inner.get_all_nodes(self.clone(), cur_ts)
}
fn queue_bucket_kick(&self, node_id: DHTKey) {
let idx = self.find_bucket_index(node_id);
self.unlocked_inner.kick_queue.lock().insert(idx);
fn queue_bucket_kicks(&self, node_ids: TypedKeySet) {
for node_id in node_ids.iter() {
let x = self.unlocked_inner.calculate_bucket_index(node_id);
self.unlocked_inner.kick_queue.lock().insert(x);
}
}
/// Create a node reference, possibly creating a bucket entry
/// the 'update_func' closure is called on the node, and, if created,
/// in a locked fashion as to ensure the bucket entry state is always valid
pub fn create_node_ref<F>(&self, node_id: DHTKey, update_func: F) -> Option<NodeRef>
where
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner),
{
/// Resolve an existing routing table entry using any crypto kind and return a reference to it
pub fn lookup_any_node_ref(&self, node_id_key: PublicKey) -> Option<NodeRef> {
self.inner
.write()
.create_node_ref(self.clone(), node_id, update_func)
.read()
.lookup_any_node_ref(self.clone(), node_id_key)
}
/// Resolve an existing routing table entry and return a reference to it
pub fn lookup_node_ref(&self, node_id: DHTKey) -> Option<NodeRef> {
pub fn lookup_node_ref(&self, node_id: TypedKey) -> Option<NodeRef> {
self.inner.read().lookup_node_ref(self.clone(), node_id)
}
/// Resolve an existing routing table entry and return a filtered reference to it
pub fn lookup_and_filter_noderef(
&self,
node_id: DHTKey,
node_id: TypedKey,
routing_domain_set: RoutingDomainSet,
dial_info_filter: DialInfoFilter,
) -> Option<NodeRef> {
@ -524,18 +625,16 @@ impl RoutingTable {
/// Shortcut function to add a node to our routing table if it doesn't exist
/// and add the dial info we have for it. Returns a noderef filtered to
/// the routing domain in which this node was registered for convenience.
pub fn register_node_with_signed_node_info(
pub fn register_node_with_peer_info(
&self,
routing_domain: RoutingDomain,
node_id: DHTKey,
signed_node_info: SignedNodeInfo,
peer_info: PeerInfo,
allow_invalid: bool,
) -> Option<NodeRef> {
self.inner.write().register_node_with_signed_node_info(
self.inner.write().register_node_with_peer_info(
self.clone(),
routing_domain,
node_id,
signed_node_info,
peer_info,
allow_invalid,
)
}
@ -544,7 +643,7 @@ impl RoutingTable {
/// and add the last peer address we have for it, since that's pretty common
pub fn register_node_with_existing_connection(
&self,
node_id: DHTKey,
node_id: TypedKey,
descriptor: ConnectionDescriptor,
timestamp: Timestamp,
) -> Option<NodeRef> {
@ -563,7 +662,7 @@ impl RoutingTable {
self.inner.read().get_routing_table_health()
}
pub fn get_recent_peers(&self) -> Vec<(DHTKey, RecentPeersEntry)> {
pub fn get_recent_peers(&self) -> Vec<(TypedKey, RecentPeersEntry)> {
let mut recent_peers = Vec::new();
let mut dead_peers = Vec::new();
let mut out = Vec::new();
@ -602,7 +701,7 @@ impl RoutingTable {
out
}
pub fn touch_recent_peer(&self, node_id: DHTKey, last_connection: ConnectionDescriptor) {
pub fn touch_recent_peer(&self, node_id: TypedKey, last_connection: ConnectionDescriptor) {
self.inner
.write()
.touch_recent_peer(node_id, last_connection)
@ -651,7 +750,7 @@ impl RoutingTable {
dial_info_filter: DialInfoFilter,
) -> RoutingTableEntryFilter<'a> {
// does it have matching public dial info?
Box::new(move |rti, _k, e| {
Box::new(move |rti, e| {
if let Some(e) = e {
e.with(rti, |_rti, e| {
if let Some(ni) = e.node_info(routing_domain) {
@ -679,7 +778,7 @@ impl RoutingTable {
dial_info: DialInfo,
) -> RoutingTableEntryFilter<'a> {
// does the node's outbound capabilities match the dialinfo?
Box::new(move |rti, _k, e| {
Box::new(move |rti, e| {
if let Some(e) = e {
e.with(rti, |_rti, e| {
if let Some(ni) = e.node_info(routing_domain) {
@ -709,27 +808,37 @@ impl RoutingTable {
.find_fast_public_nodes_filtered(self.clone(), node_count, filters)
}
/// Retrieve up to N of each type of protocol capable nodes
pub fn find_bootstrap_nodes_filtered(&self, max_per_type: usize) -> Vec<NodeRef> {
/// Retrieve up to N of each type of protocol capable nodes for a single crypto kind
fn find_bootstrap_nodes_filtered_per_crypto_kind(
&self,
crypto_kind: CryptoKind,
max_per_type: usize,
) -> Vec<NodeRef> {
let protocol_types = vec![
ProtocolType::UDP,
ProtocolType::TCP,
ProtocolType::WS,
ProtocolType::WSS,
];
let protocol_types_len = protocol_types.len();
let mut nodes_proto_v4 = vec![0usize, 0usize, 0usize, 0usize];
let mut nodes_proto_v6 = vec![0usize, 0usize, 0usize, 0usize];
let filter = Box::new(
move |rti: &RoutingTableInner, _k: DHTKey, v: Option<Arc<BucketEntry>>| {
let entry = v.unwrap();
move |rti: &RoutingTableInner, entry: Option<Arc<BucketEntry>>| {
let entry = entry.unwrap();
entry.with(rti, |_rti, e| {
// skip nodes on our local network here
if e.has_node_info(RoutingDomain::LocalNetwork.into()) {
return false;
}
// Ensure crypto kind is supported
if !e.crypto_kinds().contains(&crypto_kind) {
return false;
}
// does it have some dial info we need?
let filter = |n: &NodeInfo| {
let mut keep = false;
@ -769,12 +878,33 @@ impl RoutingTable {
self.find_fastest_nodes(
protocol_types_len * 2 * max_per_type,
filters,
|_rti, k: DHTKey, v: Option<Arc<BucketEntry>>| {
NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
|_rti, entry: Option<Arc<BucketEntry>>| {
NodeRef::new(self.clone(), entry.unwrap().clone(), None)
},
)
}
/// Retrieve up to N of each type of protocol capable nodes for all crypto kinds
pub fn find_bootstrap_nodes_filtered(&self, max_per_type: usize) -> Vec<NodeRef> {
let mut out =
self.find_bootstrap_nodes_filtered_per_crypto_kind(VALID_CRYPTO_KINDS[0], max_per_type);
// Merge list of nodes so we don't have duplicates
for crypto_kind in &VALID_CRYPTO_KINDS[1..] {
let nrs =
self.find_bootstrap_nodes_filtered_per_crypto_kind(*crypto_kind, max_per_type);
'nrloop: for nr in nrs {
for nro in &out {
if nro.same_entry(&nr) {
continue 'nrloop;
}
}
out.push(nr);
}
}
out
}
pub fn find_peers_with_sort_and_filter<C, T, O>(
&self,
node_count: usize,
@ -786,10 +916,10 @@ impl RoutingTable {
where
C: for<'a, 'b> FnMut(
&'a RoutingTableInner,
&'b (DHTKey, Option<Arc<BucketEntry>>),
&'b (DHTKey, Option<Arc<BucketEntry>>),
&'b Option<Arc<BucketEntry>>,
&'b Option<Arc<BucketEntry>>,
) -> core::cmp::Ordering,
T: for<'r> FnMut(&'r RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> O + Send,
T: for<'r> FnMut(&'r RoutingTableInner, Option<Arc<BucketEntry>>) -> O + Send,
{
self.inner
.read()
@ -803,7 +933,7 @@ impl RoutingTable {
transform: T,
) -> Vec<O>
where
T: for<'r> FnMut(&'r RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> O + Send,
T: for<'r> FnMut(&'r RoutingTableInner, Option<Arc<BucketEntry>>) -> O + Send,
{
self.inner
.read()
@ -812,44 +942,42 @@ impl RoutingTable {
pub fn find_closest_nodes<'a, T, O>(
&self,
node_id: DHTKey,
node_count: usize,
node_id: TypedKey,
filters: VecDeque<RoutingTableEntryFilter>,
transform: T,
) -> Vec<O>
where
T: for<'r> FnMut(&'r RoutingTableInner, DHTKey, Option<Arc<BucketEntry>>) -> O + Send,
T: for<'r> FnMut(&'r RoutingTableInner, Option<Arc<BucketEntry>>) -> O + Send,
{
self.inner
.read()
.find_closest_nodes(node_id, filters, transform)
.find_closest_nodes(node_count, node_id, filters, transform)
}
#[instrument(level = "trace", skip(self), ret)]
pub fn register_find_node_answer(&self, peers: Vec<PeerInfo>) -> Vec<NodeRef> {
let node_id = self.node_id();
// register nodes we'd found
pub fn register_find_node_answer(
&self,
crypto_kind: CryptoKind,
peers: Vec<PeerInfo>,
) -> Vec<NodeRef> {
// Register nodes we'd found
let mut out = Vec::<NodeRef>::with_capacity(peers.len());
for p in peers {
// if our own node if is in the list then ignore it, as we don't add ourselves to our own routing table
if p.node_id.key == node_id {
// Ensure we're getting back nodes we asked for
if !p.node_ids.kinds().contains(&crypto_kind) {
continue;
}
// node can not be its own relay
if let Some(rid) = &p.signed_node_info.relay_id() {
if rid.key == p.node_id.key {
continue;
}
// Don't register our own node
if self.matches_own_node_id(&p.node_ids) {
continue;
}
// register the node if it's new
if let Some(nr) = self.register_node_with_signed_node_info(
RoutingDomain::PublicInternet,
p.node_id.key,
p.signed_node_info.clone(),
false,
) {
// Register the node if it's new
if let Some(nr) =
self.register_node_with_peer_info(RoutingDomain::PublicInternet, p, false)
{
out.push(nr);
}
}
@ -860,7 +988,7 @@ impl RoutingTable {
pub async fn find_node(
&self,
node_ref: NodeRef,
node_id: DHTKey,
node_id: TypedKey,
) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
let rpc_processor = self.rpc_processor();
@ -873,29 +1001,41 @@ impl RoutingTable {
// register nodes we'd found
Ok(NetworkResult::value(
self.register_find_node_answer(res.answer),
self.register_find_node_answer(node_id.kind, res.answer),
))
}
/// Ask a remote node to list the nodes it has around the current node
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn find_self(&self, node_ref: NodeRef) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
let node_id = self.node_id();
self.find_node(node_ref, node_id).await
pub async fn find_self(
&self,
crypto_kind: CryptoKind,
node_ref: NodeRef,
) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
let self_node_id = self.node_id(crypto_kind);
self.find_node(node_ref, self_node_id).await
}
/// Ask a remote node to list the nodes it has around itself
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn find_target(&self, node_ref: NodeRef) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
let node_id = node_ref.node_id();
self.find_node(node_ref, node_id).await
pub async fn find_target(
&self,
crypto_kind: CryptoKind,
node_ref: NodeRef,
) -> EyreResult<NetworkResult<Vec<NodeRef>>> {
let Some(target_node_id) = node_ref.node_ids().get(crypto_kind) else {
bail!("no target node ids for this crypto kind");
};
self.find_node(node_ref, target_node_id).await
}
#[instrument(level = "trace", skip(self))]
pub async fn reverse_find_node(&self, node_ref: NodeRef, wide: bool) {
// Ask bootstrap node to 'find' our own node so we can get some more nodes near ourselves
pub async fn reverse_find_node(&self, crypto_kind: CryptoKind, node_ref: NodeRef, wide: bool) {
// Ask node to 'find node' on own node so we can get some more nodes near ourselves
// and then contact those nodes to inform -them- that we exist
// Ask bootstrap server for nodes closest to our own node
let closest_nodes = network_result_value_or_log!(match self.find_self(node_ref.clone()).await {
// Ask node for nodes closest to our own node
let closest_nodes = network_result_value_or_log!(match self.find_self(crypto_kind, node_ref.clone()).await {
Err(e) => {
log_rtab!(error
"find_self failed for {:?}: {:?}",
@ -911,7 +1051,7 @@ impl RoutingTable {
// Ask each node near us to find us as well
if wide {
for closest_nr in closest_nodes {
network_result_value_or_log!(match self.find_self(closest_nr.clone()).await {
network_result_value_or_log!(match self.find_self(crypto_kind, closest_nr.clone()).await {
Err(e) => {
log_rtab!(error
"find_self failed for {:?}: {:?}",
@ -986,12 +1126,12 @@ impl RoutingTable {
// Go through all entries and find fastest entry that matches filter function
let inner = self.inner.read();
let inner = &*inner;
let mut best_inbound_relay: Option<(DHTKey, Arc<BucketEntry>)> = None;
let mut best_inbound_relay: Option<Arc<BucketEntry>> = None;
// Iterate all known nodes for candidates
inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, k, v| {
let v2 = v.clone();
v.with(rti, |rti, e| {
inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, entry| {
let entry2 = entry.clone();
entry.with(rti, |rti, e| {
// Ensure we have the node's status
if let Some(node_status) = e.node_status(routing_domain) {
// Ensure the node will relay
@ -999,18 +1139,18 @@ impl RoutingTable {
// Compare against previous candidate
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
// Less is faster
let better = best_inbound_relay.1.with(rti, |_rti, best| {
let better = best_inbound_relay.with(rti, |_rti, best| {
// choose low latency stability for relays
BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best)
== std::cmp::Ordering::Less
});
// Now apply filter function and see if this node should be included
if better && relay_node_filter(e) {
*best_inbound_relay = (k, v2);
*best_inbound_relay = entry2;
}
} else if relay_node_filter(e) {
// Always store the first candidate
best_inbound_relay = Some((k, v2));
best_inbound_relay = Some(entry2);
}
}
}
@ -1019,6 +1159,14 @@ impl RoutingTable {
Option::<()>::None
});
// Return the best inbound relay noderef
best_inbound_relay.map(|(k, e)| NodeRef::new(self.clone(), k, e, None))
best_inbound_relay.map(|e| NodeRef::new(self.clone(), e, None))
}
}
impl core::ops::Deref for RoutingTable {
type Target = RoutingTableUnlockedInner;
fn deref(&self) -> &Self::Target {
&self.unlocked_inner
}
}

View File

@ -6,7 +6,6 @@ use alloc::fmt;
pub struct NodeRefBaseCommon {
routing_table: RoutingTable,
node_id: DHTKey,
entry: Arc<BucketEntry>,
filter: Option<NodeRefFilter>,
sequencing: Sequencing,
@ -21,6 +20,14 @@ pub trait NodeRefBase: Sized {
fn common(&self) -> &NodeRefBaseCommon;
fn common_mut(&mut self) -> &mut NodeRefBaseCommon;
// Comparators
fn same_entry<T: NodeRefBase>(&self, other: &T) -> bool {
Arc::ptr_eq(&self.common().entry, &other.common().entry)
}
fn same_bucket_entry(&self, entry: &Arc<BucketEntry>) -> bool {
Arc::ptr_eq(&self.common().entry, entry)
}
// Implementation-specific operators
fn operate<T, F>(&self, f: F) -> T
where
@ -99,8 +106,11 @@ pub trait NodeRefBase: Sized {
fn routing_table(&self) -> RoutingTable {
self.common().routing_table.clone()
}
fn node_id(&self) -> DHTKey {
self.common().node_id
fn node_ids(&self) -> TypedKeySet {
self.operate(|_rti, e| e.node_ids())
}
fn best_node_id(&self) -> TypedKey {
self.operate(|_rti, e| e.best_node_id())
}
fn has_updated_since_last_network_change(&self) -> bool {
self.operate(|_rti, e| e.has_updated_since_last_network_change())
@ -113,11 +123,17 @@ pub trait NodeRefBase: Sized {
e.update_node_status(node_status);
});
}
fn min_max_version(&self) -> Option<VersionRange> {
self.operate(|_rti, e| e.min_max_version())
fn envelope_support(&self) -> Vec<u8> {
self.operate(|_rti, e| e.envelope_support())
}
fn set_min_max_version(&self, min_max_version: VersionRange) {
self.operate_mut(|_rti, e| e.set_min_max_version(min_max_version))
fn add_envelope_version(&self, envelope_version: u8) {
self.operate_mut(|_rti, e| e.add_envelope_version(envelope_version))
}
fn set_envelope_support(&self, envelope_support: Vec<u8>) {
self.operate_mut(|_rti, e| e.set_envelope_support(envelope_support))
}
fn best_envelope_version(&self) -> Option<u8> {
self.operate(|_rti, e| e.best_envelope_version())
}
fn state(&self, cur_ts: Timestamp) -> BucketEntryState {
self.operate(|_rti, e| e.state(cur_ts))
@ -128,7 +144,7 @@ pub trait NodeRefBase: Sized {
// Per-RoutingDomain accessors
fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
self.operate(|_rti, e| e.make_peer_info(self.node_id(), routing_domain))
self.operate(|_rti, e| e.make_peer_info(routing_domain))
}
fn node_info(&self, routing_domain: RoutingDomain) -> Option<NodeInfo> {
self.operate(|_rti, e| e.node_info(routing_domain).cloned())
@ -136,7 +152,7 @@ pub trait NodeRefBase: Sized {
fn signed_node_info_has_valid_signature(&self, routing_domain: RoutingDomain) -> bool {
self.operate(|_rti, e| {
e.signed_node_info(routing_domain)
.map(|sni| sni.has_valid_signature())
.map(|sni| sni.has_any_signature())
.unwrap_or(false)
})
}
@ -180,19 +196,18 @@ pub trait NodeRefBase: Sized {
self.operate_mut(|rti, e| {
e.signed_node_info(routing_domain)
.and_then(|n| n.relay_peer_info())
.and_then(|t| {
.and_then(|rpi| {
// If relay is ourselves, then return None, because we can't relay through ourselves
// and to contact this node we should have had an existing inbound connection
if t.node_id.key == rti.unlocked_inner.node_id {
if rti.unlocked_inner.matches_own_node_id(&rpi.node_ids) {
return None;
}
// Register relay node and return noderef
rti.register_node_with_signed_node_info(
rti.register_node_with_peer_info(
self.routing_table(),
routing_domain,
t.node_id.key,
t.signed_node_info,
rpi,
false,
)
})
@ -280,7 +295,7 @@ pub trait NodeRefBase: Sized {
fn set_last_connection(&self, connection_descriptor: ConnectionDescriptor, ts: Timestamp) {
self.operate_mut(|rti, e| {
e.set_last_connection(connection_descriptor, ts);
rti.touch_recent_peer(self.common().node_id, connection_descriptor);
rti.touch_recent_peer(e.best_node_id(), connection_descriptor);
})
}
@ -346,7 +361,6 @@ pub struct NodeRef {
impl NodeRef {
pub fn new(
routing_table: RoutingTable,
node_id: DHTKey,
entry: Arc<BucketEntry>,
filter: Option<NodeRefFilter>,
) -> Self {
@ -355,7 +369,6 @@ impl NodeRef {
Self {
common: NodeRefBaseCommon {
routing_table,
node_id,
entry,
filter,
sequencing: Sequencing::NoPreference,
@ -415,7 +428,6 @@ impl Clone for NodeRef {
Self {
common: NodeRefBaseCommon {
routing_table: self.common.routing_table.clone(),
node_id: self.common.node_id,
entry: self.common.entry.clone(),
filter: self.common.filter.clone(),
sequencing: self.common.sequencing,
@ -428,14 +440,14 @@ impl Clone for NodeRef {
impl fmt::Display for NodeRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.common.node_id.encode())
write!(f, "{}", self.common.entry.with_inner(|e| e.best_node_id()))
}
}
impl fmt::Debug for NodeRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NodeRef")
.field("node_id", &self.common.node_id)
.field("node_ids", &self.common.entry.with_inner(|e| e.node_ids()))
.field("filter", &self.common.filter)
.field("sequencing", &self.common.sequencing)
.finish()
@ -455,9 +467,10 @@ impl Drop for NodeRef {
.fetch_sub(1u32, Ordering::Relaxed)
- 1;
if new_ref_count == 0 {
self.common
.routing_table
.queue_bucket_kick(self.common.node_id);
// get node ids with inner unlocked because nothing could be referencing this entry now
// and we don't know when it will get dropped, possibly inside a lock
let node_ids = self.common().entry.with_inner(|e| e.node_ids());
self.common.routing_table.queue_bucket_kicks(node_ids);
}
}
}
@ -480,6 +493,10 @@ impl<'a> NodeRefLocked<'a> {
nr,
}
}
pub fn unlocked(&self) -> NodeRef {
self.nr.clone()
}
}
impl<'a> NodeRefBase for NodeRefLocked<'a> {
@ -539,6 +556,10 @@ impl<'a> NodeRefLockedMut<'a> {
nr,
}
}
pub fn unlocked(&self) -> NodeRef {
self.nr.clone()
}
}
impl<'a> NodeRefBase for NodeRefLockedMut<'a> {

View File

@ -16,20 +16,45 @@ pub struct RouteHopData {
#[derive(Clone, Debug)]
pub enum RouteNode {
/// Route node is optimized, no contact method information as this node id has been seen before
NodeId(NodeId),
NodeId(PublicKey),
/// Route node with full contact method information to ensure the peer is reachable
PeerInfo(PeerInfo),
}
impl fmt::Display for RouteNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
RouteNode::NodeId(x) => x.key.encode(),
RouteNode::PeerInfo(pi) => pi.node_id.key.encode(),
impl RouteNode {
pub fn node_ref(
&self,
routing_table: RoutingTable,
crypto_kind: CryptoKind,
) -> Option<NodeRef> {
match self {
RouteNode::NodeId(id) => {
//
routing_table.lookup_node_ref(TypedKey::new(crypto_kind, *id))
}
)
RouteNode::PeerInfo(pi) => {
//
routing_table.register_node_with_peer_info(
RoutingDomain::PublicInternet,
pi.clone(),
false,
)
}
}
}
pub fn describe(&self, crypto_kind: CryptoKind) -> String {
match self {
RouteNode::NodeId(id) => {
format!("{}", TypedKey::new(crypto_kind, *id))
}
RouteNode::PeerInfo(pi) => match pi.node_ids.get(crypto_kind) {
Some(id) => format!("{}", id),
None => {
format!("({})?{}", crypto_kind, pi.node_ids)
}
},
}
}
}
@ -57,14 +82,14 @@ pub enum PrivateRouteHops {
#[derive(Clone, Debug)]
pub struct PrivateRoute {
/// The public key used for the entire route
pub public_key: DHTKey,
pub public_key: TypedKey,
pub hop_count: u8,
pub hops: PrivateRouteHops,
}
impl PrivateRoute {
/// Empty private route is the form used when receiving the last hop
pub fn new_empty(public_key: DHTKey) -> Self {
pub fn new_empty(public_key: TypedKey) -> Self {
Self {
public_key,
hop_count: 0,
@ -72,7 +97,7 @@ impl PrivateRoute {
}
}
/// Stub route is the form used when no privacy is required, but you need to specify the destination for a safety route
pub fn new_stub(public_key: DHTKey, node: RouteNode) -> Self {
pub fn new_stub(public_key: TypedKey, node: RouteNode) -> Self {
Self {
public_key,
hop_count: 1,
@ -91,6 +116,11 @@ impl PrivateRoute {
false
}
/// Get the crypto kind in use for this route
pub fn crypto_kind(&self) -> CryptoKind {
self.public_key.kind
}
/// Remove the first unencrypted hop if possible
pub fn pop_first_hop(&mut self) -> Option<RouteNode> {
match &mut self.hops {
@ -117,15 +147,15 @@ impl PrivateRoute {
}
}
pub fn first_hop_node_id(&self) -> Option<DHTKey> {
pub fn first_hop_node_id(&self) -> Option<TypedKey> {
let PrivateRouteHops::FirstHop(pr_first_hop) = &self.hops else {
return None;
};
// Get the safety route to use from the spec
Some(match &pr_first_hop.node {
RouteNode::NodeId(n) => n.key,
RouteNode::PeerInfo(p) => p.node_id.key,
RouteNode::NodeId(n) => TypedKey::new(self.public_key.kind, *n),
RouteNode::PeerInfo(p) => p.node_ids.get(self.public_key.kind).unwrap(),
})
}
}
@ -138,8 +168,13 @@ impl fmt::Display for PrivateRoute {
self.public_key,
self.hop_count,
match &self.hops {
PrivateRouteHops::FirstHop(fh) => {
format!("->{}", fh.node)
PrivateRouteHops::FirstHop(_) => {
format!(
"->{}",
self.first_hop_node_id()
.map(|n| n.to_string())
.unwrap_or_else(|| "None".to_owned())
)
}
PrivateRouteHops::Data(_) => {
"->?".to_owned()
@ -162,13 +197,14 @@ pub enum SafetyRouteHops {
#[derive(Clone, Debug)]
pub struct SafetyRoute {
pub public_key: DHTKey,
pub public_key: TypedKey,
pub hop_count: u8,
pub hops: SafetyRouteHops,
}
impl SafetyRoute {
pub fn new_stub(public_key: DHTKey, private_route: PrivateRoute) -> Self {
/// Stub route is the form used when no privacy is required, but you need to directly contact a private route
pub fn new_stub(public_key: TypedKey, private_route: PrivateRoute) -> Self {
// First hop should have already been popped off for stubbed safety routes since
// we are sending directly to the first hop
assert!(matches!(private_route.hops, PrivateRouteHops::Data(_)));
@ -178,9 +214,16 @@ impl SafetyRoute {
hops: SafetyRouteHops::Private(private_route),
}
}
/// Check if this is a stub route
pub fn is_stub(&self) -> bool {
matches!(self.hops, SafetyRouteHops::Private(_))
}
/// Get the crypto kind in use for this route
pub fn crypto_kind(&self) -> CryptoKind {
self.public_key.kind
}
}
impl fmt::Display for SafetyRoute {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
use super::*;
mod permutation;
mod remote_private_route_info;
mod route_set_spec_detail;
mod route_spec_store;
mod route_spec_store_cache;
mod route_spec_store_content;
mod route_stats;
pub use remote_private_route_info::*;
pub use route_set_spec_detail::*;
pub use route_spec_store::*;
pub use route_spec_store_cache::*;
pub use route_spec_store_content::*;
pub use route_stats::*;
use crate::veilid_api::*;
use rkyv::{
with::Skip, Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize,
};
/// The size of the remote private route cache
const REMOTE_PRIVATE_ROUTE_CACHE_SIZE: usize = 1024;
/// Remote private route cache entries expire in 5 minutes if they haven't been used
const REMOTE_PRIVATE_ROUTE_CACHE_EXPIRY: TimestampDuration = TimestampDuration::new(300_000_000u64);
/// Amount of time a route can remain idle before it gets tested
const ROUTE_MIN_IDLE_TIME_MS: u32 = 30_000;
/// The size of the compiled route cache
const COMPILED_ROUTE_CACHE_SIZE: usize = 256;

View File

@ -0,0 +1,70 @@
use super::*;
/// number of route permutations is the number of unique orderings
/// for a set of nodes, given that the first node is fixed
fn _get_route_permutation_count(hop_count: usize) -> usize {
if hop_count == 0 {
unreachable!();
}
// a single node or two nodes is always fixed
if hop_count == 1 || hop_count == 2 {
return 1;
}
// more than two nodes has factorial permutation
// hop_count = 3 -> 2! -> 2
// hop_count = 4 -> 3! -> 6
(3..hop_count).into_iter().fold(2usize, |acc, x| acc * x)
}
pub type PermReturnType = (Vec<usize>, bool);
pub type PermFunc<'t> = Box<dyn FnMut(&[usize]) -> Option<PermReturnType> + Send + 't>;
/// get the route permutation at particular 'perm' index, starting at the 'start' index
/// for a set of 'hop_count' nodes. the first node is always fixed, and the maximum
/// number of permutations is given by get_route_permutation_count()
pub fn with_route_permutations(
hop_count: usize,
start: usize,
f: &mut PermFunc,
) -> Option<PermReturnType> {
if hop_count == 0 {
unreachable!();
}
// initial permutation
let mut permutation: Vec<usize> = Vec::with_capacity(hop_count);
for n in 0..hop_count {
permutation.push(start + n);
}
// if we have one hop or two, then there's only one permutation
if hop_count == 1 || hop_count == 2 {
return f(&permutation);
}
// heaps algorithm, but skipping the first element
fn heaps_permutation(
permutation: &mut [usize],
size: usize,
f: &mut PermFunc,
) -> Option<PermReturnType> {
if size == 1 {
return f(&permutation);
}
for i in 0..size {
let out = heaps_permutation(permutation, size - 1, f);
if out.is_some() {
return out;
}
if size % 2 == 1 {
permutation.swap(1, size);
} else {
permutation.swap(1 + i, size);
}
}
None
}
// recurse
heaps_permutation(&mut permutation, hop_count - 1, f)
}

View File

@ -0,0 +1,71 @@
use super::*;
/// What remote private routes have seen
#[derive(Debug, Clone, Default)]
pub struct RemotePrivateRouteInfo {
/// The private routes themselves
private_routes: Vec<PrivateRoute>,
/// Did this remote private route see our node info due to no safety route in use
last_seen_our_node_info_ts: Timestamp,
/// Last time this remote private route was requested for any reason (cache expiration)
last_touched_ts: Timestamp,
/// Stats
stats: RouteStats,
}
impl RemotePrivateRouteInfo {
pub fn new(private_routes: Vec<PrivateRoute>, cur_ts: Timestamp) -> Self {
RemotePrivateRouteInfo {
private_routes,
last_seen_our_node_info_ts: Timestamp::new(0),
last_touched_ts: cur_ts,
stats: RouteStats::new(cur_ts),
}
}
pub fn get_private_routes(&self) -> &[PrivateRoute] {
&self.private_routes
}
pub fn best_private_route(&self) -> Option<PrivateRoute> {
self.private_routes
.iter()
.reduce(|acc, x| {
if x.public_key < acc.public_key {
x
} else {
acc
}
})
.filter(|x| VALID_CRYPTO_KINDS.contains(&x.public_key.kind))
.cloned()
}
pub fn get_stats(&self) -> &RouteStats {
&self.stats
}
pub fn get_stats_mut(&mut self) -> &mut RouteStats {
&mut self.stats
}
pub fn has_seen_our_node_info_ts(&mut self, our_node_info_ts: Timestamp) -> bool {
self.last_seen_our_node_info_ts == our_node_info_ts
}
pub fn set_last_seen_our_node_info_ts(&mut self, last_seen_our_node_info_ts: Timestamp) {
self.last_seen_our_node_info_ts = last_seen_our_node_info_ts;
}
// Check to see if this remote private route has expired
pub fn did_expire(&self, cur_ts: Timestamp) -> bool {
cur_ts.saturating_sub(self.last_touched_ts) >= REMOTE_PRIVATE_ROUTE_CACHE_EXPIRY
}
/// Start fresh if this had expired
pub fn unexpire(&mut self, cur_ts: Timestamp) {
self.last_seen_our_node_info_ts = Timestamp::new(0);
self.last_touched_ts = cur_ts;
self.stats = RouteStats::new(cur_ts);
}
/// Note when this was last used
pub fn touch(&mut self, cur_ts: Timestamp) {
self.last_touched_ts = cur_ts;
}
}

View File

@ -0,0 +1,137 @@
use super::*;
#[derive(Clone, Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C, align(8)), derive(CheckBytes))]
pub struct RouteSpecDetail {
/// Crypto kind
pub crypto_kind: CryptoKind,
/// Secret key
#[with(Skip)]
pub secret_key: SecretKey,
/// Route hops (node id keys)
pub hops: Vec<PublicKey>,
}
#[derive(Clone, Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C, align(8)), derive(CheckBytes))]
pub struct RouteSetSpecDetail {
/// Route set per crypto kind
route_set: BTreeMap<PublicKey, RouteSpecDetail>,
/// Route noderefs
#[with(Skip)]
hop_node_refs: Vec<NodeRef>,
/// Published private route, do not reuse for ephemeral routes
/// Not serialized because all routes should be re-published when restarting
#[with(Skip)]
published: bool,
/// Directions this route is guaranteed to work in
#[with(RkyvEnumSet)]
directions: DirectionSet,
/// Stability preference (prefer reliable nodes over faster)
stability: Stability,
/// Sequencing capability (connection oriented protocols vs datagram)
can_do_sequenced: bool,
/// Stats
stats: RouteStats,
}
impl RouteSetSpecDetail {
pub fn new(
cur_ts: Timestamp,
route_set: BTreeMap<PublicKey, RouteSpecDetail>,
hop_node_refs: Vec<NodeRef>,
directions: DirectionSet,
stability: Stability,
can_do_sequenced: bool,
) -> Self {
Self {
route_set,
hop_node_refs,
published: false,
directions,
stability,
can_do_sequenced,
stats: RouteStats::new(cur_ts),
}
}
pub fn get_route_by_key(&self, key: &PublicKey) -> Option<&RouteSpecDetail> {
self.route_set.get(key)
}
pub fn get_route_by_key_mut(&mut self, key: &PublicKey) -> Option<&mut RouteSpecDetail> {
self.route_set.get_mut(key)
}
pub fn get_route_set_keys(&self) -> TypedKeySet {
let mut tks = TypedKeySet::new();
for (k, v) in &self.route_set {
tks.add(TypedKey::new(v.crypto_kind, *k));
}
tks
}
pub fn get_best_route_set_key(&self) -> Option<PublicKey> {
self.get_route_set_keys().best().map(|k| k.value)
}
pub fn set_hop_node_refs(&mut self, node_refs: Vec<NodeRef>) {
self.hop_node_refs = node_refs;
}
pub fn iter_route_set(
&self,
) -> alloc::collections::btree_map::Iter<PublicKey, RouteSpecDetail> {
self.route_set.iter()
}
pub fn iter_route_set_mut(
&mut self,
) -> alloc::collections::btree_map::IterMut<PublicKey, RouteSpecDetail> {
self.route_set.iter_mut()
}
pub fn get_stats(&self) -> &RouteStats {
&self.stats
}
pub fn get_stats_mut(&mut self) -> &mut RouteStats {
&mut self.stats
}
pub fn is_published(&self) -> bool {
self.published
}
pub fn set_published(&mut self, published: bool) {
self.published = published;
}
pub fn hop_count(&self) -> usize {
self.hop_node_refs.len()
}
pub fn hop_node_ref(&self, idx: usize) -> Option<NodeRef> {
self.hop_node_refs.get(idx).cloned()
}
pub fn get_stability(&self) -> Stability {
self.stability
}
pub fn get_directions(&self) -> DirectionSet {
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,
}
}
pub fn contains_nodes(&self, nodes: &[TypedKey]) -> bool {
for tk in nodes {
for (_pk, rsd) in &self.route_set {
if rsd.crypto_kind == tk.kind && rsd.hops.contains(&tk.value) {
return true;
}
}
}
false
}
/// 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) -> Vec<u8> {
let hops = &self.hop_node_refs;
let mut cache: Vec<u8> = Vec::with_capacity(hops.len() * PUBLIC_KEY_LENGTH);
for hop in hops {
cache.extend_from_slice(&hop.locked(rti).best_node_id().value.bytes);
}
cache
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,365 @@
use super::*;
// Compiled route key for caching
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct CompiledRouteCacheKey {
sr_pubkey: PublicKey,
pr_pubkey: PublicKey,
}
/// Compiled route (safety route + private route)
#[derive(Clone, Debug)]
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: SecretKey,
/// The node ref to the first hop in the compiled route
pub first_hop: NodeRef,
}
/// Ephemeral data used to help the RouteSpecStore operate efficiently
#[derive(Debug)]
pub struct RouteSpecStoreCache {
/// How many times nodes have been used
used_nodes: HashMap<PublicKey, usize>,
/// How many times nodes have been used at the terminal point of a route
used_end_nodes: HashMap<PublicKey, usize>,
/// Route spec hop cache, used to quickly disqualify routes
hop_cache: HashSet<Vec<u8>>,
/// Remote private routes we've imported and statistics
remote_private_route_set_cache: LruCache<RouteId, RemotePrivateRouteInfo>,
/// Remote private routes indexed by public key
remote_private_routes_by_key: HashMap<PublicKey, RouteId>,
/// Compiled route cache
compiled_route_cache: LruCache<CompiledRouteCacheKey, SafetyRoute>,
/// List of dead allocated routes
dead_routes: Vec<RouteId>,
/// List of dead remote routes
dead_remote_routes: Vec<RouteId>,
}
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 cache_key = rssd.make_cache_key(rti);
if !self.hop_cache.insert(cache_key) {
panic!("route should never be inserted twice");
}
for (_pk, rsd) in rssd.iter_route_set() {
for h in &rsd.hops {
self.used_nodes
.entry(*h)
.and_modify(|e| *e += 1)
.or_insert(1);
}
self.used_end_nodes
.entry(*rsd.hops.last().unwrap())
.and_modify(|e| *e += 1)
.or_insert(1);
}
}
/// checks if an allocated route is in our cache
pub fn contains_route(&self, cache_key: &Vec<u8>) -> bool {
self.hop_cache.contains(cache_key)
}
/// removes an allocated route set from our cache
pub fn remove_from_cache(
&mut self,
rti: &RoutingTableInner,
id: RouteId,
rssd: &RouteSetSpecDetail,
) -> bool {
let cache_key = rssd.make_cache_key(rti);
// Remove from hop cache
if !self.hop_cache.remove(&cache_key) {
return false;
}
for (pk, rsd) in rssd.iter_route_set() {
for h in &rsd.hops {
// Remove from used nodes cache
match self.used_nodes.entry(*h) {
std::collections::hash_map::Entry::Occupied(mut o) => {
*o.get_mut() -= 1;
if *o.get() == 0 {
o.remove();
}
}
std::collections::hash_map::Entry::Vacant(_) => {
panic!("used_nodes cache should have contained hop");
}
}
}
// Remove from end nodes cache
match self.used_end_nodes.entry(*rsd.hops.last().unwrap()) {
std::collections::hash_map::Entry::Occupied(mut o) => {
*o.get_mut() -= 1;
if *o.get() == 0 {
o.remove();
}
}
std::collections::hash_map::Entry::Vacant(_) => {
panic!("used_end_nodes cache should have contained hop");
}
}
// Invalidate compiled route cache
self.invalidate_compiled_route_cache(pk);
}
// Mark it as dead for the update
self.dead_routes.push(id);
true
}
/// calculate how many times a node with a particular node id set has been used anywhere in the path of our allocated routes
pub fn get_used_node_count(&self, node_ids: &TypedKeySet) -> usize {
node_ids.iter().fold(0usize, |acc, k| {
acc + self.used_nodes.get(&k.value).cloned().unwrap_or_default()
})
}
/// calculate how many times a node with a particular node id set has been used at the end of the path of our allocated routes
pub fn get_used_end_node_count(&self, node_ids: &TypedKeySet) -> usize {
node_ids.iter().fold(0usize, |acc, k| {
acc + self
.used_end_nodes
.get(&k.value)
.cloned()
.unwrap_or_default()
})
}
/// add remote private route to caches
/// returns a remote private route set id
fn add_remote_private_route(
&mut self,
id: RouteId,
rprinfo: RemotePrivateRouteInfo,
) -> RouteId {
// 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());
}
let mut dead = None;
self.remote_private_route_set_cache
.insert(id, rprinfo, |dead_id, dead_rpri| {
dead = Some((dead_id, dead_rpri));
});
if let Some((dead_id, dead_rpri)) = dead {
// If anything LRUs out, remove from the by-key table
// Follow the same logic as 'remove_remote_private_route' here
for dead_private_route in dead_rpri.get_private_routes() {
self.remote_private_routes_by_key
.remove(&dead_private_route.public_key.value)
.unwrap();
self.invalidate_compiled_route_cache(&dead_private_route.public_key.value);
}
self.dead_remote_routes.push(dead_id);
}
id
}
/// get count of remote private routes in cache
pub fn get_remote_private_route_count(&self) -> usize {
self.remote_private_route_set_cache.len()
}
/// iterate all of the remote private routes we have in the cache
pub fn iter_remote_private_routes(
&self,
) -> hashlink::linked_hash_map::Iter<RouteId, RemotePrivateRouteInfo> {
self.remote_private_route_set_cache.iter()
}
/// remote private route cache accessor
/// will LRU entries and may expire entries and not return them if they are stale
pub fn get_remote_private_route(
&mut self,
cur_ts: Timestamp,
id: &RouteId,
) -> Option<&RemotePrivateRouteInfo> {
if let Some(rpri) = self.remote_private_route_set_cache.get_mut(id) {
if !rpri.did_expire(cur_ts) {
rpri.touch(cur_ts);
return Some(rpri);
}
}
None
}
/// mutable remote private route cache accessor
/// will LRU entries and may expire entries and not return them if they are stale
pub fn get_remote_private_route_mut(
&mut self,
cur_ts: Timestamp,
id: &RouteId,
) -> Option<&mut RemotePrivateRouteInfo> {
if let Some(rpri) = self.remote_private_route_set_cache.get_mut(id) {
if !rpri.did_expire(cur_ts) {
rpri.touch(cur_ts);
return Some(rpri);
}
}
None
}
/// mutable remote private route cache accessor without lru action
/// will not LRU entries but may expire entries and not return them if they are stale
pub fn peek_remote_private_route_mut(
&mut self,
cur_ts: Timestamp,
id: &RouteId,
) -> Option<&mut RemotePrivateRouteInfo> {
if let Some(rpri) = self.remote_private_route_set_cache.peek_mut(id) {
if !rpri.did_expire(cur_ts) {
rpri.touch(cur_ts);
return Some(rpri);
}
}
None
}
/// 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: &PublicKey) -> Option<RouteId> {
self.remote_private_routes_by_key.get(key).cloned()
}
/// get or create a remote private route cache entry
/// may LRU and/or expire other cache entries to make room for the new one
/// or update an existing entry with the same private route set
/// returns the route set id
pub fn cache_remote_private_route(
&mut self,
cur_ts: Timestamp,
id: RouteId,
private_routes: Vec<PrivateRoute>,
) {
// get id for this route set
if let Some(rpri) = self.get_remote_private_route_mut(cur_ts, &id) {
if rpri.did_expire(cur_ts) {
// Start fresh if this had expired
rpri.unexpire(cur_ts);
} else {
// If not expired, just mark as being used
rpri.touch(cur_ts);
}
} else {
// New remote private route cache entry
let rpri = RemotePrivateRouteInfo::new(private_routes, cur_ts);
self.add_remote_private_route(id, rpri);
if self.peek_remote_private_route_mut(cur_ts, &id).is_none() {
panic!("remote private route should exist");
};
};
}
/// remove a remote private route from the cache
pub fn remove_remote_private_route(&mut self, id: RouteId) -> bool {
let Some(rprinfo) = self.remote_private_route_set_cache.remove(&id) else {
return false;
};
for private_route in rprinfo.get_private_routes() {
self.remote_private_routes_by_key
.remove(&private_route.public_key.value)
.unwrap();
self.invalidate_compiled_route_cache(&private_route.public_key.value);
}
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: PublicKey, safety_route: SafetyRoute) {
let key = CompiledRouteCacheKey {
sr_pubkey: safety_route.public_key.value,
pr_pubkey,
};
if let Some(v) = self
.compiled_route_cache
.insert(key, safety_route, |_k, _v| {
// Do nothing on LRU evict
})
{
log_rtab!(error "route cache already contained key: sr_pubkey={:?}, pr_pubkey={:?}", v.public_key, pr_pubkey);
}
}
/// Looks up an existing compiled route from the safety and private route components
pub fn lookup_compiled_route_cache(
&mut self,
sr_pubkey: PublicKey,
pr_pubkey: PublicKey,
) -> Option<SafetyRoute> {
let key = CompiledRouteCacheKey {
sr_pubkey,
pr_pubkey,
};
self.compiled_route_cache.get(&key).cloned()
}
/// When routes are dropped, they should be removed from the compiled route cache
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 {
dead_entries.push(k.clone());
}
}
for d in dead_entries {
self.compiled_route_cache.remove(&d);
}
}
/// Take the dead local and remote routes so we can update clients
pub fn take_dead_routes(&mut self) -> Option<(Vec<RouteId>, Vec<RouteId>)> {
if self.dead_routes.is_empty() && self.dead_remote_routes.is_empty() {
// Nothing to do
return None;
}
let dead_routes = core::mem::take(&mut self.dead_routes);
let dead_remote_routes = core::mem::take(&mut self.dead_remote_routes);
Some((dead_routes, dead_remote_routes))
}
/// Clean up imported remote routes
/// Resets statistics for when our node info changes
pub fn reset_remote_private_routes(&mut self) {
// Restart stats for routes so we test the route again
for (_k, v) in self.remote_private_route_set_cache.iter_mut() {
v.get_stats_mut().reset();
}
}
/// Roll transfer statistics
pub fn roll_transfers(&mut self, last_ts: Timestamp, cur_ts: Timestamp) {
for (_k, v) in self.remote_private_route_set_cache.iter_mut() {
v.get_stats_mut().roll_transfers(last_ts, cur_ts);
}
}
}
impl Default for RouteSpecStoreCache {
fn default() -> Self {
Self {
used_nodes: Default::default(),
used_end_nodes: Default::default(),
hop_cache: Default::default(),
remote_private_route_set_cache: LruCache::new(REMOTE_PRIVATE_ROUTE_CACHE_SIZE),
remote_private_routes_by_key: HashMap::new(),
compiled_route_cache: LruCache::new(COMPILED_ROUTE_CACHE_SIZE),
dead_routes: Default::default(),
dead_remote_routes: Default::default(),
}
}
}

View File

@ -0,0 +1,177 @@
use super::*;
/// The core representation of the RouteSpecStore that can be serialized
#[derive(Debug, Clone, Default, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C, align(8)), derive(CheckBytes))]
pub struct RouteSpecStoreContent {
/// All of the route sets we have allocated so far indexed by key
id_by_key: HashMap<PublicKey, RouteId>,
/// All of the route sets we have allocated so far
details: HashMap<RouteId, RouteSetSpecDetail>,
}
impl RouteSpecStoreContent {
pub fn new() -> Self {
Self {
id_by_key: HashMap::new(),
details: HashMap::new(),
}
}
pub async fn load(routing_table: RoutingTable) -> EyreResult<RouteSpecStoreContent> {
// Deserialize what we can
let table_store = routing_table.network_manager().table_store();
let rsstdb = table_store.open("RouteSpecStore", 1).await?;
let mut content: RouteSpecStoreContent =
rsstdb.load_rkyv(0, b"content")?.unwrap_or_default();
// Look up all route hop noderefs since we can't serialize those
let mut dead_ids = Vec::new();
for (rsid, rssd) in content.details.iter_mut() {
// Get best route since they all should resolve
let Some(pk) = rssd.get_best_route_set_key() else {
dead_ids.push(rsid.clone());
continue;
};
let Some(rsd) = rssd.get_route_by_key(&pk) else {
dead_ids.push(rsid.clone());
continue;
};
// Go through best route and resolve noderefs
let mut hop_node_refs = Vec::with_capacity(rsd.hops.len());
for h in &rsd.hops {
let Some(nr) = routing_table.lookup_node_ref(TypedKey::new(rsd.crypto_kind, *h)) else {
dead_ids.push(rsid.clone());
break;
};
hop_node_refs.push(nr);
}
// Apply noderefs
rssd.set_hop_node_refs(hop_node_refs);
}
for id in dead_ids {
log_rtab!(debug "no entry, killing off private route: {}", id);
content.remove_detail(&id);
}
// Load secrets from pstore
let pstore = routing_table.network_manager().protected_store();
let secret_key_map: HashMap<PublicKey, SecretKey> = pstore
.load_user_secret_rkyv("RouteSpecStore")
.await?
.unwrap_or_default();
// Ensure we got secret keys for all the public keys
let mut got_secret_key_ids = HashSet::new();
for (rsid, rssd) in content.details.iter_mut() {
let mut found_all = true;
for (pk, rsd) in rssd.iter_route_set_mut() {
if let Some(sk) = secret_key_map.get(pk) {
rsd.secret_key = *sk;
} else {
found_all = false;
break;
}
}
if found_all {
got_secret_key_ids.insert(rsid.clone());
}
}
// If we missed any, nuke those route ids
let dead_ids: Vec<RouteId> = content
.details
.keys()
.filter_map(|id| {
if !got_secret_key_ids.contains(id) {
Some(*id)
} else {
None
}
})
.collect();
for id in dead_ids {
log_rtab!(debug "missing secret key, killing off private route: {}", id);
content.remove_detail(&id);
}
Ok(content)
}
pub async fn save(&self, routing_table: RoutingTable) -> EyreResult<()> {
// Save all the fields we care about to the frozen blob in table storage
// This skips #[with(Skip)] saving the secret keys, we save them in the protected store instead
let table_store = routing_table.network_manager().table_store();
let rsstdb = table_store.open("RouteSpecStore", 1).await?;
rsstdb.store_rkyv(0, b"content", self).await?;
// Keep secrets in protected store as well
let pstore = routing_table.network_manager().protected_store();
let mut out: HashMap<PublicKey, SecretKey> = HashMap::new();
for (_rsid, rssd) in self.details.iter() {
for (pk, rsd) in rssd.iter_route_set() {
out.insert(*pk, rsd.secret_key);
}
}
let _ = pstore.save_user_secret_rkyv("RouteSpecStore", &out).await?; // ignore if this previously existed or not
Ok(())
}
pub fn add_detail(&mut self, id: RouteId, detail: RouteSetSpecDetail) {
assert!(!self.details.contains_key(&id));
// also store in id by key table
for (pk, _) in detail.iter_route_set() {
self.id_by_key.insert(*pk, id.clone());
}
self.details.insert(id.clone(), detail);
}
pub fn remove_detail(&mut self, id: &RouteId) -> Option<RouteSetSpecDetail> {
let detail = self.details.remove(id)?;
for (pk, _) in detail.iter_route_set() {
self.id_by_key.remove(&pk).unwrap();
}
Some(detail)
}
pub fn get_detail_count(&self) -> usize {
self.details.len()
}
pub fn get_detail(&self, id: &RouteId) -> Option<&RouteSetSpecDetail> {
self.details.get(id)
}
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: &PublicKey) -> Option<RouteId> {
self.id_by_key.get(key).cloned()
}
pub fn iter_ids(&self) -> std::collections::hash_map::Keys<RouteId, RouteSetSpecDetail> {
self.details.keys()
}
pub fn iter_details(&self) -> std::collections::hash_map::Iter<RouteId, RouteSetSpecDetail> {
self.details.iter()
}
/// Clean up local allocated routes
/// Resets publication status and statistics for when our node info changes
/// Routes must be republished
pub fn reset_details(&mut self) {
for (_k, v) in &mut self.details {
// Must republish route now
v.set_published(false);
// Restart stats for routes so we test the route again
v.get_stats_mut().reset();
}
}
/// Roll transfer statistics
pub fn roll_transfers(&mut self, last_ts: Timestamp, cur_ts: Timestamp) {
for rssd in self.details.values_mut() {
rssd.get_stats_mut().roll_transfers(last_ts, cur_ts);
}
}
}

View File

@ -0,0 +1,129 @@
use super::*;
#[derive(Clone, Debug, Default, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct RouteStats {
/// Consecutive failed to send count
#[with(Skip)]
pub failed_to_send: u32,
/// Questions lost
#[with(Skip)]
pub questions_lost: u32,
/// Timestamp of when the route was created
pub created_ts: Timestamp,
/// Timestamp of when the route was last checked for validity
#[with(Skip)]
pub last_tested_ts: Option<Timestamp>,
/// Timestamp of when the route was last sent to
#[with(Skip)]
pub last_sent_ts: Option<Timestamp>,
/// Timestamp of when the route was last received over
#[with(Skip)]
pub last_received_ts: Option<Timestamp>,
/// Transfers up and down
pub transfer_stats_down_up: TransferStatsDownUp,
/// Latency stats
pub latency_stats: LatencyStats,
/// Accounting mechanism for this route's RPC latency
#[with(Skip)]
latency_stats_accounting: LatencyStatsAccounting,
/// Accounting mechanism for the bandwidth across this route
#[with(Skip)]
transfer_stats_accounting: TransferStatsAccounting,
}
impl RouteStats {
/// Make new route stats
pub fn new(created_ts: Timestamp) -> Self {
Self {
created_ts,
..Default::default()
}
}
/// Mark a route as having failed to send
pub fn record_send_failed(&mut self) {
self.failed_to_send += 1;
}
/// Mark a route as having lost a question
pub fn record_question_lost(&mut self) {
self.questions_lost += 1;
}
/// Mark a route as having received something
pub fn record_received(&mut self, cur_ts: Timestamp, bytes: ByteCount) {
self.last_received_ts = Some(cur_ts);
self.last_tested_ts = Some(cur_ts);
self.transfer_stats_accounting.add_down(bytes);
}
/// Mark a route as having been sent to
pub fn record_sent(&mut self, cur_ts: Timestamp, bytes: ByteCount) {
self.last_sent_ts = Some(cur_ts);
self.transfer_stats_accounting.add_up(bytes);
}
/// Mark a route as having been sent to
pub fn record_latency(&mut self, latency: TimestampDuration) {
self.latency_stats = self.latency_stats_accounting.record_latency(latency);
}
/// Mark a route as having been tested
pub fn record_tested(&mut self, cur_ts: Timestamp) {
self.last_tested_ts = Some(cur_ts);
// Reset question_lost and failed_to_send if we test clean
self.failed_to_send = 0;
self.questions_lost = 0;
}
/// Roll transfers for these route stats
pub fn roll_transfers(&mut self, last_ts: Timestamp, cur_ts: Timestamp) {
self.transfer_stats_accounting.roll_transfers(
last_ts,
cur_ts,
&mut self.transfer_stats_down_up,
)
}
/// Get the latency stats
pub fn latency_stats(&self) -> &LatencyStats {
&self.latency_stats
}
/// Get the transfer stats
pub fn transfer_stats(&self) -> &TransferStatsDownUp {
&self.transfer_stats_down_up
}
/// Reset stats when network restarts
pub fn reset(&mut self) {
self.last_tested_ts = None;
self.last_sent_ts = None;
self.last_received_ts = None;
}
/// Check if a route needs testing
pub fn needs_testing(&self, cur_ts: Timestamp) -> bool {
// Has the route had any failures lately?
if self.questions_lost > 0 || self.failed_to_send > 0 {
// If so, always test
return true;
}
// Has the route been tested within the idle time we'd want to check things?
// (also if we've received successfully over the route, this will get set)
if let Some(last_tested_ts) = self.last_tested_ts {
if cur_ts.saturating_sub(last_tested_ts)
> TimestampDuration::new(ROUTE_MIN_IDLE_TIME_MS as u64 * 1000u64)
{
return true;
}
} else {
// If this route has never been tested, it needs to be
return true;
}
false
}
}

View File

@ -102,7 +102,7 @@ impl RoutingDomainEditor {
let mut changed = false;
{
let node_id = self.routing_table.node_id();
let node_ids = self.routing_table.node_ids();
let mut inner = self.routing_table.inner.write();
inner.with_routing_domain_mut(self.routing_domain, |detail| {
@ -134,9 +134,7 @@ impl RoutingDomainEditor {
info!(
"{:?} Dial Info: {}@{}",
self.routing_domain,
NodeId::new(node_id),
dial_info_detail.dial_info
self.routing_domain, node_ids, dial_info_detail.dial_info
);
changed = true;
}

View File

@ -10,13 +10,13 @@ pub enum ContactMethod {
/// Contact the node directly
Direct(DialInfo),
/// Request via signal the node connect back directly (relay, target)
SignalReverse(DHTKey, DHTKey),
/// Request via signal the node negotiate a hole punch (relay, target_node)
SignalHolePunch(DHTKey, DHTKey),
SignalReverse(TypedKey, TypedKey),
/// Request via signal the node negotiate a hole punch (relay, target)
SignalHolePunch(TypedKey, TypedKey),
/// Must use an inbound relay to reach the node
InboundRelay(DHTKey),
InboundRelay(TypedKey),
/// Must use outbound relay to reach the node
OutboundRelay(DHTKey),
OutboundRelay(TypedKey),
}
#[derive(Debug)]
@ -106,8 +106,8 @@ impl RoutingDomainDetailCommon {
network_class: self.network_class.unwrap_or(NetworkClass::Invalid),
outbound_protocols: self.outbound_protocols,
address_types: self.address_types,
min_version: MIN_CRYPTO_VERSION,
max_version: MAX_CRYPTO_VERSION,
envelope_support: VALID_ENVELOPE_VERSIONS.to_vec(),
crypto_support: VALID_CRYPTO_KINDS.to_vec(),
dial_info_detail_list: self.dial_info_details.clone(),
};
@ -118,7 +118,7 @@ impl RoutingDomainDetailCommon {
let opt_relay_pi = rn.locked(rti).make_peer_info(self.routing_domain);
if let Some(relay_pi) = opt_relay_pi {
match relay_pi.signed_node_info {
SignedNodeInfo::Direct(d) => Some((relay_pi.node_id, d)),
SignedNodeInfo::Direct(d) => Some((relay_pi.node_ids, d)),
SignedNodeInfo::Relayed(_) => {
warn!("relay node should not have a relay itself! if this happens, a relay updated its signed node info and became a relay, which should cause the relay to be dropped");
None
@ -130,27 +130,27 @@ impl RoutingDomainDetailCommon {
});
let signed_node_info = match relay_info {
Some((relay_id, relay_sdni)) => SignedNodeInfo::Relayed(
SignedRelayedNodeInfo::with_secret(
NodeId::new(rti.unlocked_inner.node_id),
Some((relay_ids, relay_sdni)) => SignedNodeInfo::Relayed(
SignedRelayedNodeInfo::make_signatures(
rti.unlocked_inner.crypto(),
rti.unlocked_inner.node_id_typed_key_pairs(),
node_info,
relay_id,
relay_ids,
relay_sdni,
&rti.unlocked_inner.node_id_secret,
)
.unwrap(),
),
None => SignedNodeInfo::Direct(
SignedDirectNodeInfo::with_secret(
NodeId::new(rti.unlocked_inner.node_id),
SignedDirectNodeInfo::make_signatures(
rti.unlocked_inner.crypto(),
rti.unlocked_inner.node_id_typed_key_pairs(),
node_info,
&rti.unlocked_inner.node_id_secret,
)
.unwrap()
),
};
PeerInfo::new(NodeId::new(rti.unlocked_inner.node_id), signed_node_info)
PeerInfo::new(rti.unlocked_inner.node_ids(), signed_node_info)
}
pub fn with_peer_info<F, R>(&self, rti: &RoutingTableInner, f: F) -> R
@ -280,7 +280,17 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
// Get the nodeinfos for convenience
let node_a = peer_a.signed_node_info.node_info();
let node_b = peer_b.signed_node_info.node_info();
// Get the node ids that would be used between these peers
let cck = common_crypto_kinds(&peer_a.node_ids.kinds(), &peer_b.node_ids.kinds());
let Some(best_ck) = cck.first().copied() else {
// No common crypto kinds between these nodes, can't contact
return ContactMethod::Unreachable;
};
//let node_a_id = peer_a.node_ids.get(best_ck).unwrap();
let node_b_id = peer_b.node_ids.get(best_ck).unwrap();
// Get the best match dial info for node B if we have it
if let Some(target_did) =
first_filtered_dial_info_detail(node_a, node_b, &dial_info_filter, sequencing)
@ -293,15 +303,20 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
// Get the target's inbound relay, it must have one or it is not reachable
if let Some(node_b_relay) = peer_b.signed_node_info.relay_info() {
let node_b_relay_id = peer_b.signed_node_info.relay_id().unwrap();
// 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_id.key == peer_a.node_id.key {
if peer_b.signed_node_info.relay_ids().contains_any(&peer_a.node_ids) {
return ContactMethod::Existing;
}
// Get best node id to contact relay with
let Some(node_b_relay_id) = peer_b.signed_node_info.relay_ids().get(best_ck) else {
// No best relay id
return ContactMethod::Unreachable;
};
// Can node A reach the inbound relay directly?
if first_filtered_dial_info_detail(
node_a,
@ -329,8 +344,8 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
// Can we receive a direct reverse connection?
if !reverse_did.class.requires_signal() {
return ContactMethod::SignalReverse(
node_b_relay_id.key,
peer_b.node_id.key,
node_b_relay_id,
node_b_id,
);
}
}
@ -361,8 +376,8 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
{
// The target and ourselves have a udp dialinfo that they can reach
return ContactMethod::SignalHolePunch(
node_b_relay_id.key,
peer_a.node_id.key,
node_b_relay_id,
node_b_id,
);
}
}
@ -370,21 +385,26 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
// Otherwise we have to inbound relay
}
return ContactMethod::InboundRelay(node_b_relay_id.key);
return ContactMethod::InboundRelay(node_b_relay_id);
}
}
}
// If the node B has no direct dial info, it needs to have an inbound relay
else if let Some(node_b_relay) = peer_b.signed_node_info.relay_info() {
let node_b_relay_id = peer_b.signed_node_info.relay_id().unwrap();
// 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_id.key == peer_a.node_id.key {
if peer_b.signed_node_info.relay_ids().contains_any(&peer_a.node_ids) {
return ContactMethod::Existing;
}
// Get best node id to contact relay with
let Some(node_b_relay_id) = peer_b.signed_node_info.relay_ids().get(best_ck) else {
// No best relay id
return ContactMethod::Unreachable;
};
// Can we reach the full relay?
if first_filtered_dial_info_detail(
node_a,
@ -394,13 +414,13 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
)
.is_some()
{
return ContactMethod::InboundRelay(node_b_relay_id.key);
return ContactMethod::InboundRelay(node_b_relay_id);
}
}
// If node A can't reach the node by other means, it may need to use its own relay
if let Some(node_a_relay_id) = peer_a.signed_node_info.relay_id() {
return ContactMethod::OutboundRelay(node_a_relay_id.key);
if let Some(node_a_relay_id) = peer_a.signed_node_info.relay_ids().get(best_ck) {
return ContactMethod::OutboundRelay(node_a_relay_id);
}
ContactMethod::Unreachable

File diff suppressed because it is too large Load Diff

View File

@ -3,23 +3,123 @@ use super::*;
use futures_util::stream::{FuturesUnordered, StreamExt};
use stop_token::future::FutureExt as StopFutureExt;
pub const BOOTSTRAP_TXT_VERSION: u8 = 0;
pub const BOOTSTRAP_TXT_VERSION_0: u8 = 0;
#[derive(Clone, Debug)]
pub struct BootstrapRecord {
min_version: u8,
max_version: u8,
node_ids: TypedKeySet,
envelope_support: Vec<u8>,
dial_info_details: Vec<DialInfoDetail>,
}
pub type BootstrapRecordMap = BTreeMap<DHTKey, BootstrapRecord>;
impl BootstrapRecord {
pub fn merge(&mut self, other: BootstrapRecord) {
self.node_ids.add_all(&other.node_ids);
for x in other.envelope_support {
if !self.envelope_support.contains(&x) {
self.envelope_support.push(x);
self.envelope_support.sort();
}
}
for did in other.dial_info_details {
if !self.dial_info_details.contains(&did) {
self.dial_info_details.push(did);
}
}
}
}
impl RoutingTable {
/// Process bootstrap version 0
async fn process_bootstrap_records_v0(
&self,
records: Vec<String>,
) -> EyreResult<Option<BootstrapRecord>> {
// Bootstrap TXT Record Format Version 0:
// txt_version|envelope_support|node_ids|hostname|dialinfoshort*
//
// Split bootstrap node record by '|' and then lists by ','. Example:
// 0|0|VLD0:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ|bootstrap-1.dev.veilid.net|T5150,U5150,W5150/ws
if records.len() != 5 {
bail!("invalid number of fields in bootstrap v0 txt record");
}
// Envelope support
let mut envelope_support = Vec::new();
for ess in records[1].split(",") {
let ess = ess.trim();
let es = match ess.parse::<u8>() {
Ok(v) => v,
Err(e) => {
bail!(
"invalid envelope version specified in bootstrap node txt record: {}",
e
);
}
};
envelope_support.push(es);
}
envelope_support.dedup();
envelope_support.sort();
// Node Id
let mut node_ids = TypedKeySet::new();
for node_id_str in records[2].split(",") {
let node_id_str = node_id_str.trim();
let node_id = match TypedKey::from_str(&node_id_str) {
Ok(v) => v,
Err(e) => {
bail!(
"Invalid node id in bootstrap node record {}: {}",
node_id_str,
e
);
}
};
node_ids.add(node_id);
}
// If this is our own node id, then we skip it for bootstrap, in case we are a bootstrap node
if self.unlocked_inner.matches_own_node_id(&node_ids) {
return Ok(None);
}
// Hostname
let hostname_str = records[3].trim();
// Resolve each record and store in node dial infos list
let mut dial_info_details = Vec::new();
for rec in records[4].split(",") {
let rec = rec.trim();
let dial_infos = match DialInfo::try_vec_from_short(rec, hostname_str) {
Ok(dis) => dis,
Err(e) => {
warn!("Couldn't resolve bootstrap node dial info {}: {}", rec, e);
continue;
}
};
for di in dial_infos {
dial_info_details.push(DialInfoDetail {
dial_info: di,
class: DialInfoClass::Direct,
});
}
}
Ok(Some(BootstrapRecord {
node_ids,
envelope_support,
dial_info_details,
}))
}
// Bootstrap lookup process
#[instrument(level = "trace", skip(self), ret, err)]
pub(crate) async fn resolve_bootstrap(
&self,
bootstrap: Vec<String>,
) -> EyreResult<BootstrapRecordMap> {
) -> EyreResult<Vec<BootstrapRecord>> {
// Resolve from bootstrap root to bootstrap hostnames
let mut bsnames = Vec::<String>::new();
for bh in bootstrap {
@ -58,22 +158,14 @@ impl RoutingTable {
Ok(v) => v,
};
// for each record resolve into key/bootstraprecord pairs
let mut bootstrap_records: Vec<(DHTKey, BootstrapRecord)> = Vec::new();
let mut bootstrap_records: Vec<BootstrapRecord> = Vec::new();
for bsnirecord in bsnirecords {
// Bootstrap TXT Record Format Version 0:
// txt_version,min_version,max_version,nodeid,hostname,dialinfoshort*
//
// Split bootstrap node record by commas. Example:
// 0,0,0,7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ,bootstrap-1.dev.veilid.net,T5150,U5150,W5150/ws
// All formats split on '|' character
let records: Vec<String> = bsnirecord
.trim()
.split(',')
.split('|')
.map(|x| x.trim().to_owned())
.collect();
if records.len() < 6 {
warn!("invalid number of fields in bootstrap txt record");
continue;
}
// Bootstrap TXT record version
let txt_version: u8 = match records[0].parse::<u8>() {
@ -86,81 +178,30 @@ impl RoutingTable {
continue;
}
};
if txt_version != BOOTSTRAP_TXT_VERSION {
warn!("unsupported bootstrap txt record version");
continue;
}
// Min/Max wire protocol version
let min_version: u8 = match records[1].parse::<u8>() {
Ok(v) => v,
Err(e) => {
warn!(
"invalid min_version specified in bootstrap node txt record: {}",
e
);
continue;
}
};
let max_version: u8 = match records[2].parse::<u8>() {
Ok(v) => v,
Err(e) => {
warn!(
"invalid max_version specified in bootstrap node txt record: {}",
e
);
continue;
}
};
// Node Id
let node_id_str = &records[3];
let node_id_key = match DHTKey::try_decode(node_id_str) {
Ok(v) => v,
Err(e) => {
warn!(
"Invalid node id in bootstrap node record {}: {}",
node_id_str, e
);
continue;
}
};
// Hostname
let hostname_str = &records[4];
// If this is our own node id, then we skip it for bootstrap, in case we are a bootstrap node
if self.node_id() == node_id_key {
continue;
}
// Resolve each record and store in node dial infos list
let mut bootstrap_record = BootstrapRecord {
min_version,
max_version,
dial_info_details: Vec::new(),
};
for rec in &records[5..] {
let rec = rec.trim();
let dial_infos = match DialInfo::try_vec_from_short(rec, hostname_str) {
Ok(dis) => dis,
Err(e) => {
warn!(
"Couldn't resolve bootstrap node dial info {}: {}",
rec, e
);
continue;
let bootstrap_record = match txt_version {
BOOTSTRAP_TXT_VERSION_0 => {
match self.process_bootstrap_records_v0(records).await {
Err(e) => {
warn!(
"couldn't process v0 bootstrap records from {}: {}",
bsname, e
);
continue;
}
Ok(Some(v)) => v,
Ok(None) => {
// skipping
continue;
}
}
};
for di in dial_infos {
bootstrap_record.dial_info_details.push(DialInfoDetail {
dial_info: di,
class: DialInfoClass::Direct,
});
}
}
bootstrap_records.push((node_id_key, bootstrap_record));
_ => {
warn!("unsupported bootstrap txt record version");
continue;
}
};
bootstrap_records.push(bootstrap_record);
}
Some(bootstrap_records)
}
@ -168,21 +209,35 @@ impl RoutingTable {
);
}
let mut bsmap = BootstrapRecordMap::new();
let mut merged_bootstrap_records: Vec<BootstrapRecord> = Vec::new();
while let Some(bootstrap_records) = unord.next().await {
if let Some(bootstrap_records) = bootstrap_records {
for (bskey, mut bsrec) in bootstrap_records {
let rec = bsmap.entry(bskey).or_insert_with(|| BootstrapRecord {
min_version: bsrec.min_version,
max_version: bsrec.max_version,
dial_info_details: Vec::new(),
});
rec.dial_info_details.append(&mut bsrec.dial_info_details);
let Some(bootstrap_records) = bootstrap_records else {
continue;
};
for mut bsrec in bootstrap_records {
let mut mbi = 0;
while mbi < merged_bootstrap_records.len() {
let mbr = &mut merged_bootstrap_records[mbi];
if mbr.node_ids.contains_any(&bsrec.node_ids) {
// Merge record, pop this one out
let mbr = merged_bootstrap_records.remove(mbi);
bsrec.merge(mbr);
} else {
// No overlap, go to next record
mbi += 1;
}
}
// Append merged record
merged_bootstrap_records.push(bsrec);
}
}
Ok(bsmap)
// ensure dial infos are sorted
for mbr in &mut merged_bootstrap_records {
mbr.dial_info_details.sort();
}
Ok(merged_bootstrap_records)
}
// 'direct' bootstrap task routine for systems incapable of resolving TXT records, such as browser WASM
@ -203,21 +258,20 @@ impl RoutingTable {
// Got peer info, let's add it to the routing table
for pi in peer_info {
let k = pi.node_id.key;
// Register the node
if let Some(nr) = self.register_node_with_signed_node_info(
RoutingDomain::PublicInternet,
k,
pi.signed_node_info,
false,
) {
if let Some(nr) =
self.register_node_with_peer_info(RoutingDomain::PublicInternet, pi, false)
{
// Add this our futures to process in parallel
let routing_table = self.clone();
unord.push(
// lets ask bootstrap to find ourselves now
async move { routing_table.reverse_find_node(nr, true).await }
.instrument(Span::current()),
);
for crypto_kind in VALID_CRYPTO_KINDS {
let routing_table = self.clone();
let nr = nr.clone();
unord.push(
// lets ask bootstrap to find ourselves now
async move { routing_table.reverse_find_node(crypto_kind, nr, true).await }
.instrument(Span::current()),
);
}
}
}
}
@ -230,112 +284,100 @@ impl RoutingTable {
#[instrument(level = "trace", skip(self), err)]
pub(crate) async fn bootstrap_task_routine(self, stop_token: StopToken) -> EyreResult<()> {
let (bootstrap, bootstrap_nodes) = self.with_config(|c| {
(
c.network.bootstrap.clone(),
c.network.bootstrap_nodes.clone(),
)
});
let bootstrap = self
.unlocked_inner
.with_config(|c| c.network.routing_table.bootstrap.clone());
// Don't bother if bootstraps aren't configured
if bootstrap.is_empty() {
return Ok(());
}
log_rtab!(debug "--- bootstrap_task");
// Get counts by crypto kind
let entry_count = self.inner.read().cached_entry_counts();
// See if we are specifying a direct dialinfo for bootstrap, if so use the direct mechanism
if !bootstrap.is_empty() && bootstrap_nodes.is_empty() {
let mut bootstrap_dialinfos = Vec::<DialInfo>::new();
for b in &bootstrap {
if let Ok(bootstrap_di_vec) = DialInfo::try_vec_from_url(&b) {
for bootstrap_di in bootstrap_di_vec {
bootstrap_dialinfos.push(bootstrap_di);
}
let mut bootstrap_dialinfos = Vec::<DialInfo>::new();
for b in &bootstrap {
if let Ok(bootstrap_di_vec) = DialInfo::try_vec_from_url(&b) {
for bootstrap_di in bootstrap_di_vec {
bootstrap_dialinfos.push(bootstrap_di);
}
}
if bootstrap_dialinfos.len() > 0 {
return self
.direct_bootstrap_task_routine(stop_token, bootstrap_dialinfos)
.await;
}
}
if bootstrap_dialinfos.len() > 0 {
return self
.direct_bootstrap_task_routine(stop_token, bootstrap_dialinfos)
.await;
}
// If we aren't specifying a bootstrap node list explicitly, then pull from the bootstrap server(s)
let bsmap: BootstrapRecordMap = if !bootstrap_nodes.is_empty() {
let mut bsmap = BootstrapRecordMap::new();
let mut bootstrap_node_dial_infos = Vec::new();
for b in bootstrap_nodes {
let (id_str, di_str) = b
.split_once('@')
.ok_or_else(|| eyre!("Invalid node dial info in bootstrap entry"))?;
let node_id =
NodeId::from_str(id_str).wrap_err("Invalid node id in bootstrap entry")?;
let dial_info =
DialInfo::from_str(di_str).wrap_err("Invalid dial info in bootstrap entry")?;
bootstrap_node_dial_infos.push((node_id, dial_info));
}
for (node_id, dial_info) in bootstrap_node_dial_infos {
bsmap
.entry(node_id.key)
.or_insert_with(|| BootstrapRecord {
min_version: MIN_CRYPTO_VERSION,
max_version: MAX_CRYPTO_VERSION,
dial_info_details: Vec::new(),
})
.dial_info_details
.push(DialInfoDetail {
dial_info,
class: DialInfoClass::Direct, // Bootstraps are always directly reachable
});
}
bsmap
} else {
// Resolve bootstrap servers and recurse their TXT entries
self.resolve_bootstrap(bootstrap).await?
};
// Map all bootstrap entries to a single key with multiple dialinfo
// If not direct, resolve bootstrap servers and recurse their TXT entries
let bsrecs = self.resolve_bootstrap(bootstrap).await?;
// Run all bootstrap operations concurrently
let mut unord = FuturesUnordered::new();
for (k, mut v) in bsmap {
// Sort dial info so we get the preferred order correct
v.dial_info_details.sort();
for bsrec in bsrecs {
log_rtab!(
"--- bootstrapping {} with {:?}",
&bsrec.node_ids,
&bsrec.dial_info_details
);
log_rtab!("--- bootstrapping {} with {:?}", k.encode(), &v);
// Get crypto support from list of node ids
let crypto_support = bsrec.node_ids.kinds();
// Make invalid signed node info (no signature)
if let Some(nr) = self.register_node_with_signed_node_info(
RoutingDomain::PublicInternet,
k,
SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo {
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
outbound_protocols: ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled
address_types: AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable
min_version: v.min_version, // Minimum crypto version specified in txt record
max_version: v.max_version, // Maximum crypto version specified in txt record
dial_info_detail_list: v.dial_info_details, // Dial info is as specified in the bootstrap list
})),
true,
) {
// Make unsigned SignedNodeInfo
let sni = SignedNodeInfo::Direct(SignedDirectNodeInfo::with_no_signature(NodeInfo {
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
outbound_protocols: ProtocolTypeSet::only(ProtocolType::UDP), // Bootstraps do not participate in relaying and will not make outbound requests, but will have UDP enabled
address_types: AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable
envelope_support: bsrec.envelope_support, // Envelope support is as specified in the bootstrap list
crypto_support, // Crypto support is derived from list of node ids
dial_info_detail_list: bsrec.dial_info_details, // Dial info is as specified in the bootstrap list
}));
let pi = PeerInfo::new(bsrec.node_ids, sni);
if let Some(nr) =
self.register_node_with_peer_info(RoutingDomain::PublicInternet, pi, true)
{
// Add this our futures to process in parallel
let routing_table = self.clone();
unord.push(
async move {
// Need VALID signed peer info, so ask bootstrap to find_node of itself
// which will ensure it has the bootstrap's signed peer info as part of the response
let _ = routing_table.find_target(nr.clone()).await;
// Ensure we got the signed peer info
if !nr.signed_node_info_has_valid_signature(RoutingDomain::PublicInternet) {
log_rtab!(warn
"bootstrap at {:?} did not return valid signed node info",
nr
);
// If this node info is invalid, it will time out after being unpingable
} else {
// otherwise this bootstrap is valid, lets ask it to find ourselves now
routing_table.reverse_find_node(nr, true).await
}
for crypto_kind in VALID_CRYPTO_KINDS {
// Do we need to bootstrap this crypto kind?
let eckey = (RoutingDomain::PublicInternet, crypto_kind);
let cnt = entry_count.get(&eckey).copied().unwrap_or_default();
if cnt != 0 {
continue;
}
.instrument(Span::current()),
);
// Bootstrap this crypto kind
let nr = nr.clone();
let routing_table = self.clone();
unord.push(
async move {
// Need VALID signed peer info, so ask bootstrap to find_node of itself
// which will ensure it has the bootstrap's signed peer info as part of the response
let _ = routing_table.find_target(crypto_kind, nr.clone()).await;
// Ensure we got the signed peer info
if !nr
.signed_node_info_has_valid_signature(RoutingDomain::PublicInternet)
{
log_rtab!(warn
"bootstrap at {:?} did not return valid signed node info",
nr
);
// If this node info is invalid, it will time out after being unpingable
} else {
// otherwise this bootstrap is valid, lets ask it to find ourselves now
routing_table.reverse_find_node(crypto_kind, nr, true).await
}
}
.instrument(Span::current()),
);
}
}
}

View File

@ -10,12 +10,13 @@ impl RoutingTable {
_last_ts: Timestamp,
cur_ts: Timestamp,
) -> EyreResult<()> {
let kick_queue: Vec<usize> = core::mem::take(&mut *self.unlocked_inner.kick_queue.lock())
.into_iter()
.collect();
let kick_queue: Vec<BucketIndex> =
core::mem::take(&mut *self.unlocked_inner.kick_queue.lock())
.into_iter()
.collect();
let mut inner = self.inner.write();
for idx in kick_queue {
inner.kick_bucket(idx)
for bucket_index in kick_queue {
inner.kick_bucket(bucket_index)
}
Ok(())
}

View File

@ -134,21 +134,30 @@ impl RoutingTable {
self.unlocked_inner.kick_buckets_task.tick().await?;
}
// See how many live PublicInternet entries we have
let live_public_internet_entry_count = self.get_entry_count(
RoutingDomain::PublicInternet.into(),
BucketEntryState::Unreliable,
);
// Refresh entry counts
let entry_counts = {
let mut inner = self.inner.write();
inner.refresh_cached_entry_counts()
};
let min_peer_count = self.with_config(|c| c.network.dht.min_peer_count as usize);
// If none, then add the bootstrap nodes to it
if live_public_internet_entry_count == 0 {
// Figure out which tables need bootstrap or peer minimum refresh
let mut needs_bootstrap = false;
let mut needs_peer_minimum_refresh = false;
for ck in VALID_CRYPTO_KINDS {
let eckey = (RoutingDomain::PublicInternet, ck);
let cnt = entry_counts.get(&eckey).copied().unwrap_or_default();
if cnt == 0 {
needs_bootstrap = true;
} else if cnt < min_peer_count {
needs_peer_minimum_refresh = true;
}
}
if needs_bootstrap {
self.unlocked_inner.bootstrap_task.tick().await?;
}
// If we still don't have enough peers, find nodes until we do
else if !self.unlocked_inner.bootstrap_task.is_running()
&& live_public_internet_entry_count < min_peer_count
{
if needs_peer_minimum_refresh {
self.unlocked_inner.peer_minimum_refresh_task.tick().await?;
}

View File

@ -16,26 +16,60 @@ impl RoutingTable {
self,
stop_token: StopToken,
) -> EyreResult<()> {
// Get counts by crypto kind
let entry_count = self.inner.read().cached_entry_counts();
let min_peer_count = self.with_config(|c| c.network.dht.min_peer_count as usize);
// For the PublicInternet routing domain, get list of all peers we know about
// even the unreliable ones, and ask them to find nodes close to our node too
let routing_table = self.clone();
let noderefs = routing_table.find_fastest_nodes(
min_peer_count,
VecDeque::new(),
|_rti, k: DHTKey, v: Option<Arc<BucketEntry>>| {
NodeRef::new(routing_table.clone(), k, v.unwrap().clone(), None)
},
);
let mut ord = FuturesOrdered::new();
for nr in noderefs {
for crypto_kind in VALID_CRYPTO_KINDS {
// Do we need to peer minimum refresh this crypto kind?
let eckey = (RoutingDomain::PublicInternet, crypto_kind);
let cnt = entry_count.get(&eckey).copied().unwrap_or_default();
if cnt == 0 || cnt > min_peer_count {
// If we have enough nodes, skip it
// If we have zero nodes, bootstrap will get it
continue;
}
let routing_table = self.clone();
ord.push_back(
async move { routing_table.reverse_find_node(nr, false).await }
.instrument(Span::current()),
let mut filters = VecDeque::new();
let filter = Box::new(
move |_rti: &RoutingTableInner, opt_entry: Option<Arc<BucketEntry>>| {
// Keep only the entries that contain the crypto kind we're looking for
if let Some(entry) = opt_entry {
entry.with_inner(|e| e.crypto_kinds().contains(&crypto_kind))
} else {
VALID_CRYPTO_KINDS.contains(&crypto_kind)
}
},
) as RoutingTableEntryFilter;
filters.push_front(filter);
let noderefs = routing_table.find_fastest_nodes(
min_peer_count,
filters,
|_rti, entry: Option<Arc<BucketEntry>>| {
NodeRef::new(routing_table.clone(), entry.unwrap().clone(), None)
},
);
for nr in noderefs {
let routing_table = self.clone();
ord.push_back(
async move {
routing_table
.reverse_find_node(crypto_kind, nr, false)
.await
}
.instrument(Span::current()),
);
}
}
// do peer minimum search in order from fastest to slowest

View File

@ -25,7 +25,6 @@ impl RoutingTable {
// Get the PublicInternet relay if we are using one
let opt_relay_nr = self.relay_node(RoutingDomain::PublicInternet);
let opt_relay_id = opt_relay_nr.map(|nr| nr.node_id());
// Get our publicinternet dial info
let dids = self.all_filtered_dial_info_details(
@ -35,38 +34,42 @@ impl RoutingTable {
// For all nodes needing pings, figure out how many and over what protocols
for nr in node_refs {
// If this is a relay, let's check for NAT keepalives
// If this is our relay, let's check for NAT keepalives
let mut did_pings = false;
if Some(nr.node_id()) == opt_relay_id {
// Relay nodes get pinged over all protocols we have inbound dialinfo for
// This is so we can preserve the inbound NAT mappings at our router
for did in &dids {
// Do we need to do this ping?
// Check if we have already pinged over this low-level-protocol/address-type/port combo
// We want to ensure we do the bare minimum required here
let pt = did.dial_info.protocol_type();
let at = did.dial_info.address_type();
let needs_ping = if let Some((llpt, port)) =
mapped_port_info.protocol_to_port.get(&(pt, at))
{
mapped_port_info
.low_level_protocol_ports
.remove(&(*llpt, at, *port))
} else {
false
};
if needs_ping {
let rpc = rpc.clone();
let dif = did.dial_info.make_filter();
let nr_filtered =
nr.filtered_clone(NodeRefFilter::new().with_dial_info_filter(dif));
log_net!("--> Keepalive ping to {:?}", nr_filtered);
unord.push(
async move { rpc.rpc_call_status(Destination::direct(nr_filtered)).await }
if let Some(relay_nr) = &opt_relay_nr {
if nr.same_entry(relay_nr) {
// Relay nodes get pinged over all protocols we have inbound dialinfo for
// This is so we can preserve the inbound NAT mappings at our router
for did in &dids {
// Do we need to do this ping?
// Check if we have already pinged over this low-level-protocol/address-type/port combo
// We want to ensure we do the bare minimum required here
let pt = did.dial_info.protocol_type();
let at = did.dial_info.address_type();
let needs_ping = if let Some((llpt, port)) =
mapped_port_info.protocol_to_port.get(&(pt, at))
{
mapped_port_info
.low_level_protocol_ports
.remove(&(*llpt, at, *port))
} else {
false
};
if needs_ping {
let rpc = rpc.clone();
let dif = did.dial_info.make_filter();
let nr_filtered =
nr.filtered_clone(NodeRefFilter::new().with_dial_info_filter(dif));
log_net!("--> Keepalive ping to {:?}", nr_filtered);
unord.push(
async move {
rpc.rpc_call_status(Destination::direct(nr_filtered)).await
}
.instrument(Span::current())
.boxed(),
);
did_pings = true;
);
did_pings = true;
}
}
}
}

View File

@ -8,7 +8,7 @@ const BACKGROUND_SAFETY_ROUTE_COUNT: usize = 2;
impl RoutingTable {
/// Fastest routes sort
fn route_sort_latency_fn(a: &(DHTKey, u64), b: &(DHTKey, u64)) -> cmp::Ordering {
fn route_sort_latency_fn(a: &(RouteId, u64), b: &(RouteId, u64)) -> cmp::Ordering {
let mut al = a.1;
let mut bl = b.1;
// Treat zero latency as uncalculated
@ -35,14 +35,14 @@ impl RoutingTable {
///
/// If a route doesn't 'need_testing', then we neither test nor drop it
#[instrument(level = "trace", skip(self))]
fn get_allocated_routes_to_test(&self, cur_ts: Timestamp) -> Vec<DHTKey> {
fn get_allocated_routes_to_test(&self, cur_ts: Timestamp) -> Vec<RouteId> {
let default_route_hop_count =
self.with_config(|c| c.network.rpc.default_route_hop_count as usize);
let rss = self.route_spec_store();
let mut must_test_routes = Vec::<DHTKey>::new();
let mut unpublished_routes = Vec::<(DHTKey, u64)>::new();
let mut expired_routes = Vec::<DHTKey>::new();
let mut must_test_routes = Vec::<RouteId>::new();
let mut unpublished_routes = Vec::<(RouteId, u64)>::new();
let mut expired_routes = Vec::<RouteId>::new();
rss.list_allocated_routes(|k, v| {
let stats = v.get_stats();
// Ignore nodes that don't need testing
@ -81,7 +81,7 @@ impl RoutingTable {
}
// Process dead routes
for r in &expired_routes {
for r in expired_routes {
log_rtab!(debug "Expired route: {}", r);
rss.release_route(r);
}
@ -95,7 +95,7 @@ impl RoutingTable {
async fn test_route_set(
&self,
stop_token: StopToken,
routes_needing_testing: Vec<DHTKey>,
routes_needing_testing: Vec<RouteId>,
) -> EyreResult<()> {
if routes_needing_testing.is_empty() {
return Ok(());
@ -107,43 +107,45 @@ impl RoutingTable {
#[derive(Default, Debug)]
struct TestRouteContext {
failed: bool,
dead_routes: Vec<DHTKey>,
dead_routes: Vec<RouteId>,
}
let mut unord = FuturesUnordered::new();
let ctx = Arc::new(Mutex::new(TestRouteContext::default()));
for r in routes_needing_testing {
let rss = rss.clone();
let ctx = ctx.clone();
unord.push(
async move {
let success = match rss.test_route(&r).await {
Ok(v) => v,
Err(e) => {
log_rtab!(error "Test route failed: {}", e);
ctx.lock().failed = true;
{
let mut unord = FuturesUnordered::new();
for r in routes_needing_testing {
let rss = rss.clone();
let ctx = ctx.clone();
unord.push(
async move {
let success = match rss.test_route(r).await {
Ok(v) => v,
Err(e) => {
log_rtab!(error "Test route failed: {}", e);
ctx.lock().failed = true;
return;
}
};
if success {
// Route is okay, leave it alone
return;
}
};
if success {
// Route is okay, leave it alone
return;
// Route test failed
ctx.lock().dead_routes.push(r);
}
// Route test failed
ctx.lock().dead_routes.push(r);
}
.instrument(Span::current())
.boxed(),
);
.instrument(Span::current())
.boxed(),
);
}
// Wait for test_route futures to complete in parallel
while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {}
}
// Wait for test_route futures to complete in parallel
while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {}
// Process failed routes
let ctx = &mut *ctx.lock();
for r in &ctx.dead_routes {
log_rtab!(debug "Dead route failed to test: {}", &r);
let ctx = Arc::try_unwrap(ctx).unwrap().into_inner();
for r in ctx.dead_routes {
log_rtab!(debug "Dead route failed to test: {}", r);
rss.release_route(r);
}
@ -176,13 +178,16 @@ impl RoutingTable {
.await?;
}
// Ensure we have a minimum of N allocated local, unpublished routes with the default number of hops
// Ensure we have a minimum of N allocated local, unpublished routes with the default number of hops and all our supported crypto kinds
let default_route_hop_count =
self.with_config(|c| c.network.rpc.default_route_hop_count as usize);
let mut local_unpublished_route_count = 0usize;
let rss = self.route_spec_store();
rss.list_allocated_routes(|_k, v| {
if !v.is_published() && v.hop_count() == default_route_hop_count {
if !v.is_published()
&& v.hop_count() == default_route_hop_count
&& v.get_route_set_keys().kinds() == VALID_CRYPTO_KINDS
{
local_unpublished_route_count += 1;
}
Option::<()>::None
@ -196,6 +201,7 @@ impl RoutingTable {
// Parameters here must be the default safety route spec
// These will be used by test_remote_route as well
if let Some(k) = rss.allocate_route(
&VALID_CRYPTO_KINDS,
Stability::default(),
Sequencing::default(),
default_route_hop_count,

View File

@ -51,10 +51,9 @@ impl RoutingTable {
// The outbound relay is the host of the PWA
if let Some(outbound_relay_peerinfo) = intf::get_outbound_relay_peer().await {
// Register new outbound relay
if let Some(nr) = self.register_node_with_signed_node_info(
if let Some(nr) = self.register_node_with_peer_info(
RoutingDomain::PublicInternet,
outbound_relay_peerinfo.node_id.key,
outbound_relay_peerinfo.signed_node_info,
outbound_relay_peerinfo,
false,
) {
info!("Outbound relay node selected: {}", nr);

View File

@ -21,13 +21,9 @@ impl RoutingTable {
);
// Roll all bucket entry transfers
let entries: Vec<Arc<BucketEntry>> = inner
.buckets
.iter()
.flat_map(|b| b.entries().map(|(_k, v)| v.clone()))
.collect();
for v in entries {
v.with_mut(inner, |_rti, e| e.roll_transfers(last_ts, cur_ts));
let all_entries: Vec<Arc<BucketEntry>> = inner.all_entries.iter().collect();
for entry in all_entries {
entry.with_mut(inner, |_rti, e| e.roll_transfers(last_ts, cur_ts));
}
}

View File

@ -1,7 +1,7 @@
use super::*;
use core::convert::TryInto;
pub fn decode_dht_key(public_key: &veilid_capnp::key256::Reader) -> DHTKey {
pub fn decode_key256(public_key: &veilid_capnp::key256::Reader) -> PublicKey {
let u0 = public_key.get_u0().to_be_bytes();
let u1 = public_key.get_u1().to_be_bytes();
let u2 = public_key.get_u2().to_be_bytes();
@ -13,32 +13,28 @@ pub fn decode_dht_key(public_key: &veilid_capnp::key256::Reader) -> DHTKey {
x[16..24].copy_from_slice(&u2);
x[24..32].copy_from_slice(&u3);
DHTKey::new(x)
PublicKey::new(x)
}
pub fn encode_dht_key(
key: &DHTKey,
builder: &mut veilid_capnp::key256::Builder,
) -> Result<(), RPCError> {
pub fn encode_key256(key: &PublicKey, builder: &mut veilid_capnp::key256::Builder) {
builder.set_u0(u64::from_be_bytes(
key.bytes[0..8]
.try_into()
.map_err(RPCError::map_protocol("slice with incorrect length"))?,
.expect("slice with incorrect length"),
));
builder.set_u1(u64::from_be_bytes(
key.bytes[8..16]
.try_into()
.map_err(RPCError::map_protocol("slice with incorrect length"))?,
.expect("slice with incorrect length"),
));
builder.set_u2(u64::from_be_bytes(
key.bytes[16..24]
.try_into()
.map_err(RPCError::map_protocol("slice with incorrect length"))?,
.expect("slice with incorrect length"),
));
builder.set_u3(u64::from_be_bytes(
key.bytes[24..32]
.try_into()
.map_err(RPCError::map_protocol("slice with incorrect length"))?,
.expect("slice with incorrect length"),
));
Ok(())
}

View File

@ -1,10 +1,9 @@
mod address;
mod address_type_set;
mod dht_key;
mod dht_signature;
mod dial_info;
mod dial_info_class;
mod dial_info_detail;
mod key256;
mod network_class;
mod node_info;
mod node_status;
@ -16,21 +15,22 @@ mod protocol_type_set;
mod sender_info;
mod sequencing;
mod signal_info;
mod signature512;
mod signed_direct_node_info;
mod signed_node_info;
mod signed_relayed_node_info;
mod socket_address;
mod tunnel;
mod typed_key;
mod typed_signature;
mod value_data;
mod value_key;
pub use address::*;
pub use address_type_set::*;
pub use dht_key::*;
pub use dht_signature::*;
pub use dial_info::*;
pub use dial_info_class::*;
pub use dial_info_detail::*;
pub use key256::*;
pub use network_class::*;
pub use node_info::*;
pub use node_status::*;
@ -42,12 +42,14 @@ pub use protocol_type_set::*;
pub use sender_info::*;
pub use sequencing::*;
pub use signal_info::*;
pub use signature512::*;
pub use signed_direct_node_info::*;
pub use signed_node_info::*;
pub use signed_relayed_node_info::*;
pub use socket_address::*;
pub use tunnel::*;
pub use typed_key::*;
pub use typed_signature::*;
pub use value_data::*;
pub use value_key::*;
use super::*;

View File

@ -12,8 +12,24 @@ pub fn encode_node_info(
let mut ats_builder = builder.reborrow().init_address_types();
encode_address_type_set(&node_info.address_types, &mut ats_builder)?;
builder.set_min_version(node_info.min_version);
builder.set_max_version(node_info.max_version);
let mut es_builder = builder
.reborrow()
.init_envelope_support(node_info.envelope_support.len() as u32);
if let Some(s) = es_builder.as_slice() {
s.clone_from_slice(&node_info.envelope_support);
}
let mut cs_builder = builder
.reborrow()
.init_crypto_support(node_info.crypto_support.len() as u32);
if let Some(s) = cs_builder.as_slice() {
let csvec: Vec<u32> = node_info
.crypto_support
.iter()
.map(|x| u32::from_be_bytes(x.0))
.collect();
s.clone_from_slice(&csvec);
}
let mut didl_builder = builder.reborrow().init_dial_info_detail_list(
node_info
@ -55,8 +71,51 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
.map_err(RPCError::protocol)?,
)?;
let min_version = reader.reborrow().get_min_version();
let max_version = reader.reborrow().get_max_version();
let envelope_support = reader
.reborrow()
.get_envelope_support()
.map_err(RPCError::protocol)?
.as_slice()
.map(|s| s.to_vec())
.unwrap_or_default();
// Ensure envelope versions are not duplicated
// Unsorted is okay, some nodes may have a different envelope order preference
// But nothing should show up more than once
let mut eversions = envelope_support.clone();
eversions.dedup();
if eversions.len() != envelope_support.len() {
return Err(RPCError::protocol("duplicate envelope versions"));
}
if envelope_support.len() > MAX_ENVELOPE_VERSIONS {
return Err(RPCError::protocol("too many envelope versions"));
}
if envelope_support.len() == 0 {
return Err(RPCError::protocol("no envelope versions"));
}
let crypto_support: Vec<CryptoKind> = reader
.reborrow()
.get_crypto_support()
.map_err(RPCError::protocol)?
.as_slice()
.map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect())
.unwrap_or_default();
// Ensure crypto kinds are not duplicated
// Unsorted is okay, some nodes may have a different crypto order preference
// But nothing should show up more than once
let mut ckinds = crypto_support.clone();
ckinds.dedup();
if ckinds.len() != crypto_support.len() {
return Err(RPCError::protocol("duplicate crypto kinds"));
}
if crypto_support.len() > MAX_CRYPTO_KINDS {
return Err(RPCError::protocol("too many crypto kinds"));
}
if crypto_support.len() == 0 {
return Err(RPCError::protocol("no crypto kinds"));
}
let didl_reader = reader
.reborrow()
@ -76,8 +135,8 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
network_class,
outbound_protocols,
address_types,
min_version,
max_version,
envelope_support,
crypto_support,
dial_info_detail_list,
})
}

View File

@ -2,15 +2,17 @@ use super::*;
pub fn encode_nonce(nonce: &Nonce, builder: &mut veilid_capnp::nonce24::Builder) {
builder.set_u0(u64::from_be_bytes(
nonce[0..8].try_into().expect("slice with incorrect length"),
nonce.bytes[0..8]
.try_into()
.expect("slice with incorrect length"),
));
builder.set_u1(u64::from_be_bytes(
nonce[8..16]
nonce.bytes[8..16]
.try_into()
.expect("slice with incorrect length"),
));
builder.set_u2(u64::from_be_bytes(
nonce[16..24]
nonce.bytes[16..24]
.try_into()
.expect("slice with incorrect length"),
));
@ -21,9 +23,9 @@ pub fn decode_nonce(reader: &veilid_capnp::nonce24::Reader) -> Nonce {
let u1 = reader.get_u1().to_be_bytes();
let u2 = reader.get_u2().to_be_bytes();
[
Nonce::new([
u0[0], u0[1], u0[2], u0[3], u0[4], u0[5], u0[6], u0[7], // u0
u1[0], u1[1], u1[2], u1[3], u1[4], u1[5], u1[6], u1[7], // u1
u2[0], u2[1], u2[2], u2[3], u2[4], u2[5], u2[6], u2[7], // u2
]
])
}

View File

@ -15,9 +15,12 @@ impl RPCAnswer {
pub fn desc(&self) -> &'static str {
self.detail.desc()
}
pub fn decode(reader: &veilid_capnp::answer::Reader) -> Result<RPCAnswer, RPCError> {
pub fn decode(
reader: &veilid_capnp::answer::Reader,
crypto: Crypto,
) -> Result<RPCAnswer, RPCError> {
let d_reader = reader.get_detail();
let detail = RPCAnswerDetail::decode(&d_reader)?;
let detail = RPCAnswerDetail::decode(&d_reader, crypto)?;
Ok(RPCAnswer { detail })
}
pub fn encode(&self, builder: &mut veilid_capnp::answer::Builder) -> Result<(), RPCError> {
@ -60,6 +63,7 @@ impl RPCAnswerDetail {
pub fn decode(
reader: &veilid_capnp::answer::detail::Reader,
crypto: Crypto,
) -> Result<RPCAnswerDetail, RPCError> {
let which_reader = reader.which().map_err(RPCError::protocol)?;
let out = match which_reader {
@ -70,7 +74,7 @@ impl RPCAnswerDetail {
}
veilid_capnp::answer::detail::FindNodeA(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationFindNodeA::decode(&op_reader)?;
let out = RPCOperationFindNodeA::decode(&op_reader, crypto)?;
RPCAnswerDetail::FindNodeA(out)
}
veilid_capnp::answer::detail::AppCallA(r) => {
@ -80,27 +84,27 @@ impl RPCAnswerDetail {
}
veilid_capnp::answer::detail::GetValueA(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationGetValueA::decode(&op_reader)?;
let out = RPCOperationGetValueA::decode(&op_reader, crypto)?;
RPCAnswerDetail::GetValueA(out)
}
veilid_capnp::answer::detail::SetValueA(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationSetValueA::decode(&op_reader)?;
let out = RPCOperationSetValueA::decode(&op_reader, crypto)?;
RPCAnswerDetail::SetValueA(out)
}
veilid_capnp::answer::detail::WatchValueA(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationWatchValueA::decode(&op_reader)?;
let out = RPCOperationWatchValueA::decode(&op_reader, crypto)?;
RPCAnswerDetail::WatchValueA(out)
}
veilid_capnp::answer::detail::SupplyBlockA(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationSupplyBlockA::decode(&op_reader)?;
let out = RPCOperationSupplyBlockA::decode(&op_reader, crypto)?;
RPCAnswerDetail::SupplyBlockA(out)
}
veilid_capnp::answer::detail::FindBlockA(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationFindBlockA::decode(&op_reader)?;
let out = RPCOperationFindBlockA::decode(&op_reader, crypto)?;
RPCAnswerDetail::FindBlockA(out)
}
veilid_capnp::answer::detail::StartTunnelA(r) => {

View File

@ -16,22 +16,25 @@ impl RPCOperationKind {
}
}
pub fn decode(kind_reader: &veilid_capnp::operation::kind::Reader) -> Result<Self, RPCError> {
pub fn decode(
kind_reader: &veilid_capnp::operation::kind::Reader,
crypto: Crypto,
) -> Result<Self, RPCError> {
let which_reader = kind_reader.which().map_err(RPCError::protocol)?;
let out = match which_reader {
veilid_capnp::operation::kind::Which::Question(r) => {
let q_reader = r.map_err(RPCError::protocol)?;
let out = RPCQuestion::decode(&q_reader)?;
let out = RPCQuestion::decode(&q_reader, crypto)?;
RPCOperationKind::Question(out)
}
veilid_capnp::operation::kind::Which::Statement(r) => {
let q_reader = r.map_err(RPCError::protocol)?;
let out = RPCStatement::decode(&q_reader)?;
let out = RPCStatement::decode(&q_reader, crypto)?;
RPCOperationKind::Statement(out)
}
veilid_capnp::operation::kind::Which::Answer(r) => {
let q_reader = r.map_err(RPCError::protocol)?;
let out = RPCAnswer::decode(&q_reader)?;
let out = RPCAnswer::decode(&q_reader, crypto)?;
RPCOperationKind::Answer(out)
}
};
@ -54,31 +57,25 @@ impl RPCOperationKind {
#[derive(Debug, Clone)]
pub struct RPCOperation {
op_id: OperationId,
sender_node_info: Option<SignedNodeInfo>,
opt_sender_peer_info: Option<PeerInfo>,
target_node_info_ts: Timestamp,
kind: RPCOperationKind,
}
impl RPCOperation {
pub fn new_question(
question: RPCQuestion,
sender_signed_node_info: SenderSignedNodeInfo,
) -> Self {
pub fn new_question(question: RPCQuestion, sender_peer_info: SenderPeerInfo) -> Self {
Self {
op_id: OperationId::new(get_random_u64()),
sender_node_info: sender_signed_node_info.signed_node_info,
target_node_info_ts: sender_signed_node_info.target_node_info_ts,
opt_sender_peer_info: sender_peer_info.opt_sender_peer_info,
target_node_info_ts: sender_peer_info.target_node_info_ts,
kind: RPCOperationKind::Question(question),
}
}
pub fn new_statement(
statement: RPCStatement,
sender_signed_node_info: SenderSignedNodeInfo,
) -> Self {
pub fn new_statement(statement: RPCStatement, sender_peer_info: SenderPeerInfo) -> Self {
Self {
op_id: OperationId::new(get_random_u64()),
sender_node_info: sender_signed_node_info.signed_node_info,
target_node_info_ts: sender_signed_node_info.target_node_info_ts,
opt_sender_peer_info: sender_peer_info.opt_sender_peer_info,
target_node_info_ts: sender_peer_info.target_node_info_ts,
kind: RPCOperationKind::Statement(statement),
}
}
@ -86,12 +83,12 @@ impl RPCOperation {
pub fn new_answer(
request: &RPCOperation,
answer: RPCAnswer,
sender_signed_node_info: SenderSignedNodeInfo,
sender_peer_info: SenderPeerInfo,
) -> Self {
Self {
op_id: request.op_id,
sender_node_info: sender_signed_node_info.signed_node_info,
target_node_info_ts: sender_signed_node_info.target_node_info_ts,
opt_sender_peer_info: sender_peer_info.opt_sender_peer_info,
target_node_info_ts: sender_peer_info.target_node_info_ts,
kind: RPCOperationKind::Answer(answer),
}
}
@ -100,8 +97,8 @@ impl RPCOperation {
self.op_id
}
pub fn sender_node_info(&self) -> Option<&SignedNodeInfo> {
self.sender_node_info.as_ref()
pub fn sender_peer_info(&self) -> Option<&PeerInfo> {
self.opt_sender_peer_info.as_ref()
}
pub fn target_node_info_ts(&self) -> Timestamp {
self.target_node_info_ts
@ -117,20 +114,16 @@ impl RPCOperation {
pub fn decode(
operation_reader: &veilid_capnp::operation::Reader,
opt_sender_node_id: Option<&DHTKey>,
crypto: Crypto,
) -> Result<Self, RPCError> {
let op_id = OperationId::new(operation_reader.get_op_id());
let sender_node_info = if operation_reader.has_sender_node_info() {
if let Some(sender_node_id) = opt_sender_node_id {
let sni_reader = operation_reader
.get_sender_node_info()
.map_err(RPCError::protocol)?;
let sni = decode_signed_node_info(&sni_reader, sender_node_id)?;
Some(sni)
} else {
None
}
let sender_peer_info = if operation_reader.has_sender_peer_info() {
let pi_reader = operation_reader
.get_sender_peer_info()
.map_err(RPCError::protocol)?;
let pi = decode_peer_info(&pi_reader, crypto.clone())?;
Some(pi)
} else {
None
};
@ -138,11 +131,11 @@ impl RPCOperation {
let target_node_info_ts = Timestamp::new(operation_reader.get_target_node_info_ts());
let kind_reader = operation_reader.get_kind();
let kind = RPCOperationKind::decode(&kind_reader)?;
let kind = RPCOperationKind::decode(&kind_reader, crypto)?;
Ok(RPCOperation {
op_id,
sender_node_info,
opt_sender_peer_info: sender_peer_info,
target_node_info_ts,
kind,
})
@ -150,9 +143,9 @@ impl RPCOperation {
pub fn encode(&self, builder: &mut veilid_capnp::operation::Builder) -> Result<(), RPCError> {
builder.set_op_id(self.op_id.as_u64());
if let Some(sender_info) = &self.sender_node_info {
let mut si_builder = builder.reborrow().init_sender_node_info();
encode_signed_node_info(&sender_info, &mut si_builder)?;
if let Some(sender_peer_info) = &self.opt_sender_peer_info {
let mut pi_builder = builder.reborrow().init_sender_peer_info();
encode_peer_info(&sender_peer_info, &mut pi_builder)?;
}
builder.set_target_node_info_ts(self.target_node_info_ts.as_u64());
let mut k_builder = builder.reborrow().init_kind();

View File

@ -2,7 +2,7 @@ use super::*;
#[derive(Debug, Clone)]
pub struct RPCOperationFindBlockQ {
pub block_id: DHTKey,
pub block_id: TypedKey,
}
impl RPCOperationFindBlockQ {
@ -10,7 +10,7 @@ impl RPCOperationFindBlockQ {
reader: &veilid_capnp::operation_find_block_q::Reader,
) -> Result<RPCOperationFindBlockQ, RPCError> {
let bi_reader = reader.get_block_id().map_err(RPCError::protocol)?;
let block_id = decode_dht_key(&bi_reader);
let block_id = decode_typed_key(&bi_reader)?;
Ok(RPCOperationFindBlockQ { block_id })
}
@ -19,7 +19,7 @@ impl RPCOperationFindBlockQ {
builder: &mut veilid_capnp::operation_find_block_q::Builder,
) -> Result<(), RPCError> {
let mut bi_builder = builder.reborrow().init_block_id();
encode_dht_key(&self.block_id, &mut bi_builder)?;
encode_typed_key(&self.block_id, &mut bi_builder);
Ok(())
}
@ -35,6 +35,7 @@ pub struct RPCOperationFindBlockA {
impl RPCOperationFindBlockA {
pub fn decode(
reader: &veilid_capnp::operation_find_block_a::Reader,
crypto: Crypto,
) -> Result<RPCOperationFindBlockA, RPCError> {
let data = reader.get_data().map_err(RPCError::protocol)?.to_vec();
@ -46,7 +47,7 @@ impl RPCOperationFindBlockA {
.map_err(RPCError::map_internal("too many suppliers"))?,
);
for s in suppliers_reader.iter() {
let peer_info = decode_peer_info(&s)?;
let peer_info = decode_peer_info(&s, crypto.clone())?;
suppliers.push(peer_info);
}
@ -58,7 +59,7 @@ impl RPCOperationFindBlockA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p)?;
let peer_info = decode_peer_info(&p, crypto.clone())?;
peers.push(peer_info);
}

View File

@ -2,7 +2,7 @@ use super::*;
#[derive(Debug, Clone)]
pub struct RPCOperationFindNodeQ {
pub node_id: DHTKey,
pub node_id: TypedKey,
}
impl RPCOperationFindNodeQ {
@ -10,7 +10,7 @@ impl RPCOperationFindNodeQ {
reader: &veilid_capnp::operation_find_node_q::Reader,
) -> Result<RPCOperationFindNodeQ, RPCError> {
let ni_reader = reader.get_node_id().map_err(RPCError::protocol)?;
let node_id = decode_dht_key(&ni_reader);
let node_id = decode_typed_key(&ni_reader)?;
Ok(RPCOperationFindNodeQ { node_id })
}
pub fn encode(
@ -18,7 +18,7 @@ impl RPCOperationFindNodeQ {
builder: &mut veilid_capnp::operation_find_node_q::Builder,
) -> Result<(), RPCError> {
let mut ni_builder = builder.reborrow().init_node_id();
encode_dht_key(&self.node_id, &mut ni_builder)?;
encode_typed_key(&self.node_id, &mut ni_builder);
Ok(())
}
}
@ -31,6 +31,7 @@ pub struct RPCOperationFindNodeA {
impl RPCOperationFindNodeA {
pub fn decode(
reader: &veilid_capnp::operation_find_node_a::Reader,
crypto: Crypto,
) -> Result<RPCOperationFindNodeA, RPCError> {
let peers_reader = reader.get_peers().map_err(RPCError::protocol)?;
let mut peers = Vec::<PeerInfo>::with_capacity(
@ -40,7 +41,7 @@ impl RPCOperationFindNodeA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p)?;
let peer_info = decode_peer_info(&p, crypto.clone())?;
peers.push(peer_info);
}

View File

@ -2,7 +2,8 @@ use super::*;
#[derive(Debug, Clone)]
pub struct RPCOperationGetValueQ {
pub key: ValueKey,
pub key: TypedKey,
pub subkey: ValueSubkey,
}
impl RPCOperationGetValueQ {
@ -10,15 +11,17 @@ impl RPCOperationGetValueQ {
reader: &veilid_capnp::operation_get_value_q::Reader,
) -> Result<RPCOperationGetValueQ, RPCError> {
let k_reader = reader.get_key().map_err(RPCError::protocol)?;
let key = decode_value_key(&k_reader)?;
Ok(RPCOperationGetValueQ { key })
let key = decode_typed_key(&k_reader)?;
let subkey = reader.get_subkey();
Ok(RPCOperationGetValueQ { key, subkey })
}
pub fn encode(
&self,
builder: &mut veilid_capnp::operation_get_value_q::Builder,
) -> Result<(), RPCError> {
let mut k_builder = builder.reborrow().init_key();
encode_value_key(&self.key, &mut k_builder)?;
encode_typed_key(&self.key, &mut k_builder);
builder.set_subkey(self.subkey);
Ok(())
}
}
@ -32,6 +35,7 @@ pub enum RPCOperationGetValueA {
impl RPCOperationGetValueA {
pub fn decode(
reader: &veilid_capnp::operation_get_value_a::Reader,
crypto: Crypto,
) -> Result<RPCOperationGetValueA, RPCError> {
match reader.which().map_err(RPCError::protocol)? {
veilid_capnp::operation_get_value_a::Which::Data(r) => {
@ -47,7 +51,7 @@ impl RPCOperationGetValueA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p)?;
let peer_info = decode_peer_info(&p, crypto.clone())?;
peers.push(peer_info);
}

View File

@ -2,17 +2,15 @@ use super::*;
#[derive(Debug, Clone)]
pub struct RoutedOperation {
pub version: u8,
pub sequencing: Sequencing,
pub signatures: Vec<DHTSignature>,
pub signatures: Vec<Signature>,
pub nonce: Nonce,
pub data: Vec<u8>,
}
impl RoutedOperation {
pub fn new(version: u8, sequencing: Sequencing, nonce: Nonce, data: Vec<u8>) -> Self {
pub fn new(sequencing: Sequencing, nonce: Nonce, data: Vec<u8>) -> Self {
Self {
version,
sequencing,
signatures: Vec::new(),
nonce,
@ -24,25 +22,23 @@ impl RoutedOperation {
reader: &veilid_capnp::routed_operation::Reader,
) -> Result<RoutedOperation, RPCError> {
let sigs_reader = reader.get_signatures().map_err(RPCError::protocol)?;
let mut signatures = Vec::<DHTSignature>::with_capacity(
let mut signatures = Vec::<Signature>::with_capacity(
sigs_reader
.len()
.try_into()
.map_err(RPCError::map_internal("too many signatures"))?,
);
for s in sigs_reader.iter() {
let sig = decode_signature(&s);
let sig = decode_signature512(&s);
signatures.push(sig);
}
let version = reader.get_version();
let sequencing = decode_sequencing(reader.get_sequencing().map_err(RPCError::protocol)?);
let n_reader = reader.get_nonce().map_err(RPCError::protocol)?;
let nonce = decode_nonce(&n_reader);
let data = reader.get_data().map_err(RPCError::protocol)?.to_vec();
Ok(RoutedOperation {
version,
sequencing,
signatures,
nonce,
@ -54,7 +50,6 @@ impl RoutedOperation {
&self,
builder: &mut veilid_capnp::routed_operation::Builder,
) -> Result<(), RPCError> {
builder.reborrow().set_version(self.version);
builder
.reborrow()
.set_sequencing(encode_sequencing(self.sequencing));
@ -66,7 +61,7 @@ impl RoutedOperation {
);
for (i, sig) in self.signatures.iter().enumerate() {
let mut sig_builder = sigs_builder.reborrow().get(i as u32);
encode_signature(sig, &mut sig_builder);
encode_signature512(sig, &mut sig_builder);
}
let mut n_builder = builder.reborrow().init_nonce();
encode_nonce(&self.nonce, &mut n_builder);
@ -85,9 +80,10 @@ pub struct RPCOperationRoute {
impl RPCOperationRoute {
pub fn decode(
reader: &veilid_capnp::operation_route::Reader,
crypto: Crypto,
) -> Result<RPCOperationRoute, RPCError> {
let sr_reader = reader.get_safety_route().map_err(RPCError::protocol)?;
let safety_route = decode_safety_route(&sr_reader)?;
let safety_route = decode_safety_route(&sr_reader, crypto)?;
let o_reader = reader.get_operation().map_err(RPCError::protocol)?;
let operation = RoutedOperation::decode(&o_reader)?;

View File

@ -2,7 +2,8 @@ use super::*;
#[derive(Debug, Clone)]
pub struct RPCOperationSetValueQ {
pub key: ValueKey,
pub key: TypedKey,
pub subkey: ValueSubkey,
pub value: ValueData,
}
@ -11,17 +12,19 @@ impl RPCOperationSetValueQ {
reader: &veilid_capnp::operation_set_value_q::Reader,
) -> Result<RPCOperationSetValueQ, RPCError> {
let k_reader = reader.get_key().map_err(RPCError::protocol)?;
let key = decode_value_key(&k_reader)?;
let key = decode_typed_key(&k_reader)?;
let subkey = reader.get_subkey();
let v_reader = reader.get_value().map_err(RPCError::protocol)?;
let value = decode_value_data(&v_reader)?;
Ok(RPCOperationSetValueQ { key, value })
Ok(RPCOperationSetValueQ { key, subkey, value })
}
pub fn encode(
&self,
builder: &mut veilid_capnp::operation_set_value_q::Builder,
) -> Result<(), RPCError> {
let mut k_builder = builder.reborrow().init_key();
encode_value_key(&self.key, &mut k_builder)?;
encode_typed_key(&self.key, &mut k_builder);
builder.set_subkey(self.subkey);
let mut v_builder = builder.reborrow().init_value();
encode_value_data(&self.value, &mut v_builder)?;
Ok(())
@ -37,6 +40,7 @@ pub enum RPCOperationSetValueA {
impl RPCOperationSetValueA {
pub fn decode(
reader: &veilid_capnp::operation_set_value_a::Reader,
crypto: Crypto,
) -> Result<RPCOperationSetValueA, RPCError> {
match reader.which().map_err(RPCError::protocol)? {
veilid_capnp::operation_set_value_a::Which::Data(r) => {
@ -52,7 +56,7 @@ impl RPCOperationSetValueA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p)?;
let peer_info = decode_peer_info(&p, crypto.clone())?;
peers.push(peer_info);
}

View File

@ -8,8 +8,9 @@ pub struct RPCOperationSignal {
impl RPCOperationSignal {
pub fn decode(
reader: &veilid_capnp::operation_signal::Reader,
crypto: Crypto,
) -> Result<RPCOperationSignal, RPCError> {
let signal_info = decode_signal_info(reader)?;
let signal_info = decode_signal_info(reader, crypto)?;
Ok(RPCOperationSignal { signal_info })
}
pub fn encode(

View File

@ -2,7 +2,7 @@ use super::*;
#[derive(Debug, Clone)]
pub struct RPCOperationSupplyBlockQ {
pub block_id: DHTKey,
pub block_id: TypedKey,
}
impl RPCOperationSupplyBlockQ {
@ -10,7 +10,7 @@ impl RPCOperationSupplyBlockQ {
reader: &veilid_capnp::operation_supply_block_q::Reader,
) -> Result<RPCOperationSupplyBlockQ, RPCError> {
let bi_reader = reader.get_block_id().map_err(RPCError::protocol)?;
let block_id = decode_dht_key(&bi_reader);
let block_id = decode_typed_key(&bi_reader)?;
Ok(RPCOperationSupplyBlockQ { block_id })
}
@ -19,7 +19,7 @@ impl RPCOperationSupplyBlockQ {
builder: &mut veilid_capnp::operation_supply_block_q::Builder,
) -> Result<(), RPCError> {
let mut bi_builder = builder.reborrow().init_block_id();
encode_dht_key(&self.block_id, &mut bi_builder)?;
encode_typed_key(&self.block_id, &mut bi_builder);
Ok(())
}
@ -34,6 +34,7 @@ pub enum RPCOperationSupplyBlockA {
impl RPCOperationSupplyBlockA {
pub fn decode(
reader: &veilid_capnp::operation_supply_block_a::Reader,
crypto: Crypto,
) -> Result<RPCOperationSupplyBlockA, RPCError> {
match reader.which().map_err(RPCError::protocol)? {
veilid_capnp::operation_supply_block_a::Which::Expiration(r) => {
@ -48,7 +49,7 @@ impl RPCOperationSupplyBlockA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p)?;
let peer_info = decode_peer_info(&p, crypto.clone())?;
peers.push(peer_info);
}

View File

@ -2,7 +2,9 @@ use super::*;
#[derive(Debug, Clone)]
pub struct RPCOperationValueChanged {
pub key: ValueKey,
pub key: TypedKey,
pub subkeys: Vec<ValueSubkeyRange>,
pub count: u32,
pub value: ValueData,
}
@ -11,17 +13,60 @@ impl RPCOperationValueChanged {
reader: &veilid_capnp::operation_value_changed::Reader,
) -> Result<RPCOperationValueChanged, RPCError> {
let k_reader = reader.get_key().map_err(RPCError::protocol)?;
let key = decode_value_key(&k_reader)?;
let key = decode_typed_key(&k_reader)?;
let sk_reader = reader.get_subkeys().map_err(RPCError::protocol)?;
let mut subkeys = Vec::<ValueSubkeyRange>::with_capacity(
sk_reader
.len()
.try_into()
.map_err(RPCError::map_protocol("too many subkey ranges"))?,
);
for skr in sk_reader.iter() {
let vskr = (skr.get_start(), skr.get_end());
if vskr.0 > vskr.1 {
return Err(RPCError::protocol("invalid subkey range"));
}
if let Some(lvskr) = subkeys.last() {
if lvskr.1 >= vskr.0 {
return Err(RPCError::protocol(
"subkey range out of order or not merged",
));
}
}
subkeys.push(vskr);
}
let count = reader.get_count();
let v_reader = reader.get_value().map_err(RPCError::protocol)?;
let value = decode_value_data(&v_reader)?;
Ok(RPCOperationValueChanged { key, value })
Ok(RPCOperationValueChanged {
key,
subkeys,
count,
value,
})
}
pub fn encode(
&self,
builder: &mut veilid_capnp::operation_value_changed::Builder,
) -> Result<(), RPCError> {
let mut k_builder = builder.reborrow().init_key();
encode_value_key(&self.key, &mut k_builder)?;
encode_typed_key(&self.key, &mut k_builder);
let mut sk_builder = builder.reborrow().init_subkeys(
self.subkeys
.len()
.try_into()
.map_err(RPCError::map_internal("invalid subkey range list length"))?,
);
for (i, skr) in self.subkeys.iter().enumerate() {
let mut skr_builder = sk_builder.reborrow().get(i as u32);
skr_builder.set_start(skr.0);
skr_builder.set_end(skr.1);
}
builder.set_count(self.count);
let mut v_builder = builder.reborrow().init_value();
encode_value_data(&self.value, &mut v_builder)?;
Ok(())

View File

@ -2,7 +2,10 @@ use super::*;
#[derive(Debug, Clone)]
pub struct RPCOperationWatchValueQ {
pub key: ValueKey,
pub key: TypedKey,
pub subkeys: Vec<ValueSubkeyRange>,
pub expiration: u64,
pub count: u32,
}
impl RPCOperationWatchValueQ {
@ -10,15 +13,60 @@ impl RPCOperationWatchValueQ {
reader: &veilid_capnp::operation_watch_value_q::Reader,
) -> Result<RPCOperationWatchValueQ, RPCError> {
let k_reader = reader.get_key().map_err(RPCError::protocol)?;
let key = decode_value_key(&k_reader)?;
Ok(RPCOperationWatchValueQ { key })
let key = decode_typed_key(&k_reader)?;
let sk_reader = reader.get_subkeys().map_err(RPCError::protocol)?;
let mut subkeys = Vec::<ValueSubkeyRange>::with_capacity(
sk_reader
.len()
.try_into()
.map_err(RPCError::map_protocol("too many subkey ranges"))?,
);
for skr in sk_reader.iter() {
let vskr = (skr.get_start(), skr.get_end());
if vskr.0 > vskr.1 {
return Err(RPCError::protocol("invalid subkey range"));
}
if let Some(lvskr) = subkeys.last() {
if lvskr.1 >= vskr.0 {
return Err(RPCError::protocol(
"subkey range out of order or not merged",
));
}
}
subkeys.push(vskr);
}
let expiration = reader.get_expiration();
let count = reader.get_count();
Ok(RPCOperationWatchValueQ {
key,
subkeys,
expiration,
count,
})
}
pub fn encode(
&self,
builder: &mut veilid_capnp::operation_watch_value_q::Builder,
) -> Result<(), RPCError> {
let mut k_builder = builder.reborrow().init_key();
encode_value_key(&self.key, &mut k_builder)?;
encode_typed_key(&self.key, &mut k_builder);
let mut sk_builder = builder.reborrow().init_subkeys(
self.subkeys
.len()
.try_into()
.map_err(RPCError::map_internal("invalid subkey range list length"))?,
);
for (i, skr) in self.subkeys.iter().enumerate() {
let mut skr_builder = sk_builder.reborrow().get(i as u32);
skr_builder.set_start(skr.0);
skr_builder.set_end(skr.1);
}
builder.set_expiration(self.expiration);
builder.set_count(self.count);
Ok(())
}
}
@ -32,6 +80,7 @@ pub struct RPCOperationWatchValueA {
impl RPCOperationWatchValueA {
pub fn decode(
reader: &veilid_capnp::operation_watch_value_a::Reader,
crypto: Crypto,
) -> Result<RPCOperationWatchValueA, RPCError> {
let expiration = reader.get_expiration();
let peers_reader = reader.get_peers().map_err(RPCError::protocol)?;
@ -42,7 +91,7 @@ impl RPCOperationWatchValueA {
.map_err(RPCError::map_internal("too many peers"))?,
);
for p in peers_reader.iter() {
let peer_info = decode_peer_info(&p)?;
let peer_info = decode_peer_info(&p, crypto.clone())?;
peers.push(peer_info);
}

View File

@ -19,9 +19,12 @@ impl RPCQuestion {
pub fn desc(&self) -> &'static str {
self.detail.desc()
}
pub fn decode(reader: &veilid_capnp::question::Reader) -> Result<RPCQuestion, RPCError> {
pub fn decode(
reader: &veilid_capnp::question::Reader,
crypto: Crypto,
) -> Result<RPCQuestion, RPCError> {
let rt_reader = reader.get_respond_to();
let respond_to = RespondTo::decode(&rt_reader)?;
let respond_to = RespondTo::decode(&rt_reader, crypto)?;
let d_reader = reader.get_detail();
let detail = RPCQuestionDetail::decode(&d_reader)?;
Ok(RPCQuestion { respond_to, detail })

View File

@ -23,12 +23,15 @@ impl RespondTo {
Ok(())
}
pub fn decode(reader: &veilid_capnp::question::respond_to::Reader) -> Result<Self, RPCError> {
pub fn decode(
reader: &veilid_capnp::question::respond_to::Reader,
crypto: Crypto,
) -> Result<Self, RPCError> {
let respond_to = match reader.which().map_err(RPCError::protocol)? {
veilid_capnp::question::respond_to::Sender(()) => RespondTo::Sender,
veilid_capnp::question::respond_to::PrivateRoute(pr_reader) => {
let pr_reader = pr_reader.map_err(RPCError::protocol)?;
let pr = decode_private_route(&pr_reader)?;
let pr = decode_private_route(&pr_reader, crypto)?;
RespondTo::PrivateRoute(pr)
}
};

View File

@ -18,9 +18,12 @@ impl RPCStatement {
pub fn desc(&self) -> &'static str {
self.detail.desc()
}
pub fn decode(reader: &veilid_capnp::statement::Reader) -> Result<RPCStatement, RPCError> {
pub fn decode(
reader: &veilid_capnp::statement::Reader,
crypto: Crypto,
) -> Result<RPCStatement, RPCError> {
let d_reader = reader.get_detail();
let detail = RPCStatementDetail::decode(&d_reader)?;
let detail = RPCStatementDetail::decode(&d_reader, crypto)?;
Ok(RPCStatement { detail })
}
pub fn encode(&self, builder: &mut veilid_capnp::statement::Builder) -> Result<(), RPCError> {
@ -52,6 +55,7 @@ impl RPCStatementDetail {
}
pub fn decode(
reader: &veilid_capnp::statement::detail::Reader,
crypto: Crypto,
) -> Result<RPCStatementDetail, RPCError> {
let which_reader = reader.which().map_err(RPCError::protocol)?;
let out = match which_reader {
@ -62,7 +66,7 @@ impl RPCStatementDetail {
}
veilid_capnp::statement::detail::Route(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationRoute::decode(&op_reader)?;
let out = RPCOperationRoute::decode(&op_reader, crypto)?;
RPCStatementDetail::Route(out)
}
veilid_capnp::statement::detail::ValueChanged(r) => {
@ -72,7 +76,7 @@ impl RPCStatementDetail {
}
veilid_capnp::statement::detail::Signal(r) => {
let op_reader = r.map_err(RPCError::protocol)?;
let out = RPCOperationSignal::decode(&op_reader)?;
let out = RPCOperationSignal::decode(&op_reader, crypto)?;
RPCStatementDetail::Signal(out)
}
veilid_capnp::statement::detail::ReturnReceipt(r) => {

View File

@ -5,28 +5,50 @@ pub fn encode_peer_info(
builder: &mut veilid_capnp::peer_info::Builder,
) -> Result<(), RPCError> {
//
let mut nid_builder = builder.reborrow().init_node_id();
encode_dht_key(&peer_info.node_id.key, &mut nid_builder)?;
let mut nids_builder = builder.reborrow().init_node_ids(
peer_info
.node_ids
.len()
.try_into()
.map_err(RPCError::map_invalid_format("out of bound error"))?,
);
for (i, nid) in peer_info.node_ids.iter().enumerate() {
encode_typed_key(
nid,
&mut nids_builder.reborrow().get(
i.try_into()
.map_err(RPCError::map_invalid_format("out of bound error"))?,
),
);
}
let mut sni_builder = builder.reborrow().init_signed_node_info();
encode_signed_node_info(&peer_info.signed_node_info, &mut sni_builder)?;
Ok(())
}
pub fn decode_peer_info(reader: &veilid_capnp::peer_info::Reader) -> Result<PeerInfo, RPCError> {
let nid_reader = reader
pub fn decode_peer_info(
reader: &veilid_capnp::peer_info::Reader,
crypto: Crypto,
) -> Result<PeerInfo, RPCError> {
let nids_reader = reader
.reborrow()
.get_node_id()
.get_node_ids()
.map_err(RPCError::protocol)?;
let sni_reader = reader
.reborrow()
.get_signed_node_info()
.map_err(RPCError::protocol)?;
let node_id = NodeId::new(decode_dht_key(&nid_reader));
let signed_node_info = decode_signed_node_info(&sni_reader, &node_id.key)?;
let mut node_ids = TypedKeySet::with_capacity(nids_reader.len() as usize);
for nid_reader in nids_reader.iter() {
node_ids.add(decode_typed_key(&nid_reader)?);
}
let signed_node_info = decode_signed_node_info(&sni_reader, crypto, &mut node_ids)?;
if node_ids.len() == 0 {
return Err(RPCError::protocol("no verified node ids"));
}
Ok(PeerInfo {
node_id,
node_ids,
signed_node_info,
})
}

View File

@ -53,7 +53,7 @@ pub fn encode_route_hop(
match &route_hop.node {
RouteNode::NodeId(ni) => {
let mut ni_builder = node_builder.init_node_id();
encode_dht_key(&ni.key, &mut ni_builder)?;
encode_key256(&ni, &mut ni_builder);
}
RouteNode::PeerInfo(pi) => {
let mut pi_builder = node_builder.init_peer_info();
@ -67,17 +67,20 @@ pub fn encode_route_hop(
Ok(())
}
pub fn decode_route_hop(reader: &veilid_capnp::route_hop::Reader) -> Result<RouteHop, RPCError> {
pub fn decode_route_hop(
reader: &veilid_capnp::route_hop::Reader,
crypto: Crypto,
) -> Result<RouteHop, RPCError> {
let n_reader = reader.reborrow().get_node();
let node = match n_reader.which().map_err(RPCError::protocol)? {
veilid_capnp::route_hop::node::Which::NodeId(ni) => {
let ni_reader = ni.map_err(RPCError::protocol)?;
RouteNode::NodeId(NodeId::new(decode_dht_key(&ni_reader)))
RouteNode::NodeId(decode_key256(&ni_reader))
}
veilid_capnp::route_hop::node::Which::PeerInfo(pi) => {
let pi_reader = pi.map_err(RPCError::protocol)?;
RouteNode::PeerInfo(
decode_peer_info(&pi_reader)
decode_peer_info(&pi_reader, crypto)
.map_err(RPCError::map_protocol("invalid peer info in route hop"))?,
)
}
@ -101,10 +104,10 @@ pub fn encode_private_route(
private_route: &PrivateRoute,
builder: &mut veilid_capnp::private_route::Builder,
) -> Result<(), RPCError> {
encode_dht_key(
encode_typed_key(
&private_route.public_key,
&mut builder.reborrow().init_public_key(),
)?;
);
builder.set_hop_count(private_route.hop_count);
let mut h_builder = builder.reborrow().init_hops();
match &private_route.hops {
@ -125,16 +128,17 @@ pub fn encode_private_route(
pub fn decode_private_route(
reader: &veilid_capnp::private_route::Reader,
crypto: Crypto,
) -> Result<PrivateRoute, RPCError> {
let public_key = decode_dht_key(&reader.get_public_key().map_err(RPCError::map_protocol(
"invalid public key in private route",
))?);
let public_key = decode_typed_key(&reader.get_public_key().map_err(
RPCError::map_protocol("invalid public key in private route"),
)?)?;
let hop_count = reader.get_hop_count();
let hops = match reader.get_hops().which().map_err(RPCError::protocol)? {
veilid_capnp::private_route::hops::Which::FirstHop(rh_reader) => {
let rh_reader = rh_reader.map_err(RPCError::protocol)?;
PrivateRouteHops::FirstHop(decode_route_hop(&rh_reader)?)
PrivateRouteHops::FirstHop(decode_route_hop(&rh_reader, crypto)?)
}
veilid_capnp::private_route::hops::Which::Data(rhd_reader) => {
let rhd_reader = rhd_reader.map_err(RPCError::protocol)?;
@ -156,10 +160,10 @@ pub fn encode_safety_route(
safety_route: &SafetyRoute,
builder: &mut veilid_capnp::safety_route::Builder,
) -> Result<(), RPCError> {
encode_dht_key(
encode_typed_key(
&safety_route.public_key,
&mut builder.reborrow().init_public_key(),
)?;
);
builder.set_hop_count(safety_route.hop_count);
let h_builder = builder.reborrow().init_hops();
match &safety_route.hops {
@ -178,12 +182,13 @@ pub fn encode_safety_route(
pub fn decode_safety_route(
reader: &veilid_capnp::safety_route::Reader,
crypto: Crypto,
) -> Result<SafetyRoute, RPCError> {
let public_key = decode_dht_key(
let public_key = decode_typed_key(
&reader
.get_public_key()
.map_err(RPCError::map_protocol("invalid public key in safety route"))?,
);
)?;
let hop_count = reader.get_hop_count();
let hops = match reader.get_hops().which().map_err(RPCError::protocol)? {
veilid_capnp::safety_route::hops::Which::Data(rhd_reader) => {
@ -192,7 +197,7 @@ pub fn decode_safety_route(
}
veilid_capnp::safety_route::hops::Which::Private(pr_reader) => {
let pr_reader = pr_reader.map_err(RPCError::protocol)?;
SafetyRouteHops::Private(decode_private_route(&pr_reader)?)
SafetyRouteHops::Private(decode_private_route(&pr_reader, crypto)?)
}
};

View File

@ -34,6 +34,7 @@ pub fn encode_signal_info(
pub fn decode_signal_info(
reader: &veilid_capnp::operation_signal::Reader,
crypto: Crypto,
) -> Result<SignalInfo, RPCError> {
Ok(
match reader
@ -52,7 +53,7 @@ pub fn decode_signal_info(
let pi_reader = r.get_peer_info().map_err(RPCError::map_protocol(
"invalid peer info in hole punch signal info",
))?;
let peer_info = decode_peer_info(&pi_reader)?;
let peer_info = decode_peer_info(&pi_reader, crypto)?;
SignalInfo::HolePunch { receipt, peer_info }
}
@ -68,7 +69,7 @@ pub fn decode_signal_info(
let pi_reader = r.get_peer_info().map_err(RPCError::map_protocol(
"invalid peer info in reverse connect signal info",
))?;
let peer_info = decode_peer_info(&pi_reader)?;
let peer_info = decode_peer_info(&pi_reader, crypto)?;
SignalInfo::ReverseConnect { receipt, peer_info }
}

View File

@ -1,6 +1,6 @@
use super::*;
pub fn encode_signature(sig: &DHTSignature, builder: &mut veilid_capnp::signature512::Builder) {
pub fn encode_signature512(sig: &Signature, builder: &mut veilid_capnp::signature512::Builder) {
let sig = &sig.bytes;
builder.set_u0(u64::from_be_bytes(
@ -29,7 +29,7 @@ pub fn encode_signature(sig: &DHTSignature, builder: &mut veilid_capnp::signatur
));
}
pub fn decode_signature(reader: &veilid_capnp::signature512::Reader) -> DHTSignature {
pub fn decode_signature512(reader: &veilid_capnp::signature512::Reader) -> Signature {
let u0 = reader.get_u0().to_be_bytes();
let u1 = reader.get_u1().to_be_bytes();
let u2 = reader.get_u2().to_be_bytes();
@ -39,7 +39,7 @@ pub fn decode_signature(reader: &veilid_capnp::signature512::Reader) -> DHTSigna
let u6 = reader.get_u6().to_be_bytes();
let u7 = reader.get_u7().to_be_bytes();
DHTSignature::new([
Signature::new([
u0[0], u0[1], u0[2], u0[3], u0[4], u0[5], u0[6], u0[7], // u0
u1[0], u1[1], u1[2], u1[3], u1[4], u1[5], u1[6], u1[7], // u1
u2[0], u2[1], u2[2], u2[3], u2[4], u2[5], u2[6], u2[7], // u2

View File

@ -12,18 +12,30 @@ pub fn encode_signed_direct_node_info(
.reborrow()
.set_timestamp(signed_direct_node_info.timestamp.into());
let mut sig_builder = builder.reborrow().init_signature();
let Some(signature) = &signed_direct_node_info.signature else {
return Err(RPCError::internal("Should not encode SignedDirectNodeInfo without signature!"));
};
encode_signature(signature, &mut sig_builder);
let mut sigs_builder = builder.reborrow().init_signatures(
signed_direct_node_info
.signatures
.len()
.try_into()
.map_err(RPCError::map_invalid_format("out of bound error"))?,
);
for (i, typed_signature) in signed_direct_node_info.signatures.iter().enumerate() {
encode_typed_signature(
typed_signature,
&mut sigs_builder.reborrow().get(
i.try_into()
.map_err(RPCError::map_invalid_format("out of bound error"))?,
),
);
}
Ok(())
}
pub fn decode_signed_direct_node_info(
reader: &veilid_capnp::signed_direct_node_info::Reader,
node_id: &DHTKey,
crypto: Crypto,
node_ids: &mut TypedKeySet,
) -> Result<SignedDirectNodeInfo, RPCError> {
let ni_reader = reader
.reborrow()
@ -31,15 +43,24 @@ pub fn decode_signed_direct_node_info(
.map_err(RPCError::protocol)?;
let node_info = decode_node_info(&ni_reader)?;
let sig_reader = reader
.reborrow()
.get_signature()
.map_err(RPCError::protocol)?;
let timestamp = reader.reborrow().get_timestamp().into();
let signature = decode_signature(&sig_reader);
let sigs_reader = reader
.reborrow()
.get_signatures()
.map_err(RPCError::protocol)?;
SignedDirectNodeInfo::new(NodeId::new(*node_id), node_info, timestamp, signature)
let sig_count = sigs_reader.len() as usize;
if sig_count > MAX_CRYPTO_KINDS {
return Err(RPCError::protocol("too many signatures"));
}
let mut typed_signatures = Vec::with_capacity(sig_count);
for sig_reader in sigs_reader {
let typed_signature = decode_typed_signature(&sig_reader)?;
typed_signatures.push(typed_signature);
}
SignedDirectNodeInfo::new(crypto, node_ids, node_info, timestamp, typed_signatures)
.map_err(RPCError::protocol)
}

View File

@ -20,20 +20,21 @@ pub fn encode_signed_node_info(
pub fn decode_signed_node_info(
reader: &veilid_capnp::signed_node_info::Reader,
node_id: &DHTKey,
crypto: Crypto,
node_ids: &mut TypedKeySet,
) -> Result<SignedNodeInfo, RPCError> {
match reader
.which()
.map_err(RPCError::map_internal("invalid signal operation"))?
.map_err(RPCError::map_internal("invalid signed node info"))?
{
veilid_capnp::signed_node_info::Direct(d) => {
let d_reader = d.map_err(RPCError::protocol)?;
let sdni = decode_signed_direct_node_info(&d_reader, node_id)?;
let sdni = decode_signed_direct_node_info(&d_reader, crypto, node_ids)?;
Ok(SignedNodeInfo::Direct(sdni))
}
veilid_capnp::signed_node_info::Relayed(r) => {
let r_reader = r.map_err(RPCError::protocol)?;
let srni = decode_signed_relayed_node_info(&r_reader, node_id)?;
let srni = decode_signed_relayed_node_info(&r_reader, crypto, node_ids)?;
Ok(SignedNodeInfo::Relayed(srni))
}
}

View File

@ -8,8 +8,22 @@ pub fn encode_signed_relayed_node_info(
let mut ni_builder = builder.reborrow().init_node_info();
encode_node_info(&signed_relayed_node_info.node_info, &mut ni_builder)?;
let mut rid_builder = builder.reborrow().init_relay_id();
encode_dht_key(&signed_relayed_node_info.relay_id.key, &mut rid_builder)?;
let mut rids_builder = builder.reborrow().init_relay_ids(
signed_relayed_node_info
.relay_ids
.len()
.try_into()
.map_err(RPCError::map_invalid_format("out of bound error"))?,
);
for (i, typed_key) in signed_relayed_node_info.relay_ids.iter().enumerate() {
encode_typed_key(
typed_key,
&mut rids_builder.reborrow().get(
i.try_into()
.map_err(RPCError::map_invalid_format("out of bound error"))?,
),
);
}
let mut ri_builder = builder.reborrow().init_relay_info();
encode_signed_direct_node_info(&signed_relayed_node_info.relay_info, &mut ri_builder)?;
@ -18,15 +32,30 @@ pub fn encode_signed_relayed_node_info(
.reborrow()
.set_timestamp(signed_relayed_node_info.timestamp.into());
let mut sig_builder = builder.reborrow().init_signature();
encode_signature(&signed_relayed_node_info.signature, &mut sig_builder);
let mut sigs_builder = builder.reborrow().init_signatures(
signed_relayed_node_info
.signatures
.len()
.try_into()
.map_err(RPCError::map_invalid_format("out of bound error"))?,
);
for (i, typed_signature) in signed_relayed_node_info.signatures.iter().enumerate() {
encode_typed_signature(
typed_signature,
&mut sigs_builder.reborrow().get(
i.try_into()
.map_err(RPCError::map_invalid_format("out of bound error"))?,
),
);
}
Ok(())
}
pub fn decode_signed_relayed_node_info(
reader: &veilid_capnp::signed_relayed_node_info::Reader,
node_id: &DHTKey,
crypto: Crypto,
node_ids: &mut TypedKeySet,
) -> Result<SignedRelayedNodeInfo, RPCError> {
let ni_reader = reader
.reborrow()
@ -34,33 +63,64 @@ pub fn decode_signed_relayed_node_info(
.map_err(RPCError::protocol)?;
let node_info = decode_node_info(&ni_reader)?;
let rid_reader = reader
let rids_reader = reader
.reborrow()
.get_relay_id()
.get_relay_ids()
.map_err(RPCError::protocol)?;
let relay_id = decode_dht_key(&rid_reader);
let rid_count = rids_reader.len() as usize;
if rid_count > MAX_CRYPTO_KINDS {
return Err(RPCError::protocol("too many relay ids"));
}
let mut relay_ids = TypedKeySet::with_capacity(rid_count);
for rid_reader in rids_reader {
let relay_id = decode_typed_key(&rid_reader)?;
relay_ids.add(relay_id);
}
let ri_reader = reader
.reborrow()
.get_relay_info()
.map_err(RPCError::protocol)?;
let relay_info = decode_signed_direct_node_info(&ri_reader, &relay_id)?;
let relay_info = decode_signed_direct_node_info(&ri_reader, crypto.clone(), &mut relay_ids)?;
// Ensure the relay info for the node has a superset of the crypto kinds of the node it is relaying
if common_crypto_kinds(
&node_info.crypto_support,
&relay_info.node_info.crypto_support,
)
.len()
!= node_info.crypto_support.len()
{
return Err(RPCError::protocol(
"relay should have superset of node crypto kinds",
));
}
let sig_reader = reader
.reborrow()
.get_signature()
.map_err(RPCError::protocol)?;
let timestamp = reader.reborrow().get_timestamp().into();
let signature = decode_signature(&sig_reader);
let sigs_reader = reader
.reborrow()
.get_signatures()
.map_err(RPCError::protocol)?;
let sig_count = sigs_reader.len() as usize;
if sig_count > MAX_CRYPTO_KINDS {
return Err(RPCError::protocol("too many signatures"));
}
let mut typed_signatures = Vec::with_capacity(sig_count);
for sig_reader in sigs_reader {
let typed_signature = decode_typed_signature(&sig_reader)?;
typed_signatures.push(typed_signature);
}
SignedRelayedNodeInfo::new(
NodeId::new(*node_id),
crypto,
node_ids,
node_info,
NodeId::new(relay_id),
relay_ids,
relay_info,
timestamp,
signature,
typed_signatures,
)
.map_err(RPCError::protocol)
}

View File

@ -0,0 +1,19 @@
use super::*;
pub fn decode_typed_key(typed_key: &veilid_capnp::typed_key::Reader) -> Result<TypedKey, RPCError> {
let key_reader = typed_key
.get_key()
.map_err(RPCError::map_invalid_format("invalid typed key"))?;
let kind = typed_key.get_kind();
Ok(TypedKey::new(
CryptoKind::from(kind.to_be_bytes()),
decode_key256(&key_reader),
))
}
pub fn encode_typed_key(typed_key: &TypedKey, builder: &mut veilid_capnp::typed_key::Builder) {
builder.set_kind(u32::from_be_bytes(typed_key.kind.0));
let mut key_builder = builder.reborrow().init_key();
encode_key256(&typed_key.value, &mut key_builder);
}

View File

@ -0,0 +1,24 @@
use super::*;
pub fn decode_typed_signature(
typed_signature: &veilid_capnp::typed_signature::Reader,
) -> Result<TypedSignature, RPCError> {
let sig_reader = typed_signature
.get_signature()
.map_err(RPCError::map_invalid_format("invalid typed signature"))?;
let kind = typed_signature.get_kind();
Ok(TypedSignature::new(
CryptoKind::from(kind.to_be_bytes()),
decode_signature512(&sig_reader),
))
}
pub fn encode_typed_signature(
typed_signature: &TypedSignature,
builder: &mut veilid_capnp::typed_signature::Builder,
) {
builder.set_kind(u32::from_be_bytes(typed_signature.kind.0));
let mut sig_builder = builder.reborrow().init_signature();
encode_signature512(&typed_signature.value, &mut sig_builder);
}

View File

@ -5,6 +5,7 @@ pub fn encode_value_data(
builder: &mut veilid_capnp::value_data::Builder,
) -> Result<(), RPCError> {
builder.set_data(&value_data.data);
builder.set_schema(u32::from_be_bytes(value_data.schema.0));
builder.set_seq(value_data.seq);
Ok(())
}
@ -12,5 +13,6 @@ pub fn encode_value_data(
pub fn decode_value_data(reader: &veilid_capnp::value_data::Reader) -> Result<ValueData, RPCError> {
let data = reader.get_data().map_err(RPCError::protocol)?.to_vec();
let seq = reader.get_seq();
Ok(ValueData { data, seq })
let schema = FourCC::from(reader.get_schema().to_be_bytes());
Ok(ValueData { data, schema, seq })
}

View File

@ -1,25 +0,0 @@
use super::*;
pub fn encode_value_key(
value_key: &ValueKey,
builder: &mut veilid_capnp::value_key::Builder,
) -> Result<(), RPCError> {
let mut pk_builder = builder.reborrow().init_public_key();
encode_dht_key(&value_key.key, &mut pk_builder)?;
if let Some(subkey) = &value_key.subkey {
builder.set_subkey(subkey);
}
Ok(())
}
pub fn decode_value_key(reader: &veilid_capnp::value_key::Reader) -> Result<ValueKey, RPCError> {
let pk_reader = reader.get_public_key().map_err(RPCError::protocol)?;
let key = decode_dht_key(&pk_reader);
let subkey = if !reader.has_subkey() {
None
} else {
let subkey = reader.get_subkey().map_err(RPCError::protocol)?;
Some(subkey.to_owned())
};
Ok(ValueKey { key, subkey })
}

View File

@ -15,11 +15,11 @@ pub enum Destination {
/// The relay to send to
relay: NodeRef,
/// The final destination the relay should send to
target: DHTKey,
target: NodeRef,
/// Require safety route or not
safety_selection: SafetySelection,
},
/// Send to private route (privateroute)
/// Send to private route
PrivateRoute {
/// A private route to send to
private_route: PrivateRoute,
@ -36,7 +36,7 @@ impl Destination {
safety_selection: SafetySelection::Unsafe(sequencing),
}
}
pub fn relay(relay: NodeRef, target: DHTKey) -> Self {
pub fn relay(relay: NodeRef, target: NodeRef) -> Self {
let sequencing = relay.sequencing();
Self::Relay {
relay,
@ -124,7 +124,7 @@ impl fmt::Display for Destination {
""
};
write!(f, "{}@{}{}", target.encode(), relay, sr)
write!(f, "{}@{}{}", target, relay, sr)
}
Destination::PrivateRoute {
private_route,
@ -136,7 +136,7 @@ impl fmt::Display for Destination {
""
};
write!(f, "{}{}", private_route, sr)
write!(f, "{}{}", private_route.public_key, sr)
}
}
}
@ -162,8 +162,9 @@ impl RPCProcessor {
}
SafetySelection::Safe(safety_spec) => {
// Sent directly but with a safety route, respond to private route
let crypto_kind = target.best_node_id().kind;
let Some(pr_key) = rss
.get_private_route_for_safety_spec(safety_spec, &[target.node_id()])
.get_private_route_for_safety_spec(crypto_kind, safety_spec, &target.node_ids())
.map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
};
@ -187,11 +188,15 @@ impl RPCProcessor {
}
SafetySelection::Safe(safety_spec) => {
// Sent via a relay but with a safety route, respond to private route
let crypto_kind = target.best_node_id().kind;
let mut avoid_nodes = relay.node_ids();
avoid_nodes.add_all(&target.node_ids());
let Some(pr_key) = rss
.get_private_route_for_safety_spec(safety_spec, &[relay.node_id(), *target])
.map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
};
.get_private_route_for_safety_spec(crypto_kind, safety_spec, &avoid_nodes)
.map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
};
// Get the assembled route for response
let private_route = rss
@ -205,50 +210,50 @@ impl RPCProcessor {
private_route,
safety_selection,
} => {
let Some(avoid_node_id) = private_route.first_hop_node_id() else {
return Err(RPCError::internal("destination private route must have first hop"));
};
let crypto_kind = private_route.public_key.kind;
match safety_selection {
SafetySelection::Unsafe(_) => {
// Sent to a private route with no safety route, use a stub safety route for the response
// Determine if we can use optimized nodeinfo
let route_node = match rss
.has_remote_private_route_seen_our_node_info(&private_route.public_key)
let route_node = if rss
.has_remote_private_route_seen_our_node_info(&private_route.public_key.value)
{
true => {
if !routing_table.has_valid_own_node_info(RoutingDomain::PublicInternet) {
return Ok(NetworkResult::no_connection_other("Own node info must be valid to use private route"));
}
RouteNode::NodeId(NodeId::new(routing_table.node_id()))
}
false => {
RouteNode::NodeId(routing_table.node_id(crypto_kind).value)
} else {
let Some(own_peer_info) =
routing_table.get_own_peer_info(RoutingDomain::PublicInternet) else {
return Ok(NetworkResult::no_connection_other("Own peer info must be valid to use private route"));
};
RouteNode::PeerInfo(own_peer_info)
},
};
Ok(NetworkResult::value(RespondTo::PrivateRoute(
PrivateRoute::new_stub(routing_table.node_id(), route_node),
PrivateRoute::new_stub(routing_table.node_id(crypto_kind), route_node),
)))
}
SafetySelection::Safe(safety_spec) => {
// Sent to a private route via a safety route, respond to private route
// Check for loopback test
let pr_key = if safety_spec.preferred_route
== Some(private_route.public_key)
let opt_private_route_id = rss.get_route_id_for_key(&private_route.public_key.value);
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
private_route.public_key.value
} else {
// Get the privat route to respond to that matches the safety route spec we sent the request with
// Get the private route to respond to that matches the safety route spec we sent the request with
let Some(pr_key) = rss
.get_private_route_for_safety_spec(safety_spec, &[avoid_node_id])
.get_private_route_for_safety_spec(crypto_kind, safety_spec, &[avoid_node_id])
.map_err(RPCError::internal)? else {
return Ok(NetworkResult::no_connection_other("no private route for response at this time"));
};
@ -296,17 +301,25 @@ impl RPCProcessor {
};
// Reply directly to the request's source
let sender_id = detail.envelope.get_sender_id();
let sender_node_id = TypedKey::new(detail.envelope.get_crypto_kind(), detail.envelope.get_sender_id());
// This may be a different node's reference than the 'sender' in the case of a relay
let peer_noderef = detail.peer_noderef.clone();
// If the sender_id is that of the peer, then this is a direct reply
// else it is a relayed reply through the peer
if peer_noderef.node_id() == sender_id {
if peer_noderef.node_ids().contains(&sender_node_id) {
NetworkResult::value(Destination::direct(peer_noderef))
} else {
NetworkResult::value(Destination::relay(peer_noderef, sender_id))
// Look up the sender node, we should have added it via senderNodeInfo before getting here.
if let Some(sender_noderef) = self.routing_table.lookup_node_ref(sender_node_id) {
NetworkResult::value(Destination::relay(peer_noderef, sender_noderef))
} else {
return NetworkResult::invalid_message(
"not responding to sender that has no node info",
);
}
}
}
RespondTo::PrivateRoute(pr) => {

View File

@ -52,8 +52,10 @@ struct RPCMessageHeaderDetailDirect {
/// Header details for rpc messages received over only a safety route but not a private route
#[derive(Debug, Clone)]
struct RPCMessageHeaderDetailSafetyRouted {
/// Direct header
direct: RPCMessageHeaderDetailDirect,
/// Remote safety route used
remote_safety_route: DHTKey,
remote_safety_route: PublicKey,
/// The sequencing used for this route
sequencing: Sequencing,
}
@ -61,10 +63,12 @@ struct RPCMessageHeaderDetailSafetyRouted {
/// Header details for rpc messages received over a private route
#[derive(Debug, Clone)]
struct RPCMessageHeaderDetailPrivateRouted {
/// Direct header
direct: RPCMessageHeaderDetailDirect,
/// Remote safety route used (or possibly node id the case of no safety route)
remote_safety_route: DHTKey,
remote_safety_route: PublicKey,
/// The private route we received the rpc over
private_route: DHTKey,
private_route: PublicKey,
// The safety spec for replying to this private routed rpc
safety_spec: SafetySpec,
}
@ -87,7 +91,16 @@ struct RPCMessageHeader {
detail: RPCMessageHeaderDetail,
}
impl RPCMessageHeader {}
impl RPCMessageHeader {
/// The crypto kind used on the RPC
pub fn crypto_kind(&self) -> CryptoKind {
match &self.detail {
RPCMessageHeaderDetail::Direct(d) => d.envelope.get_crypto_kind(),
RPCMessageHeaderDetail::SafetyRouted(s) => s.direct.envelope.get_crypto_kind(),
RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.envelope.get_crypto_kind(),
}
}
}
#[derive(Debug)]
pub struct RPCMessageData {
@ -141,9 +154,9 @@ struct WaitableReply {
node_ref: NodeRef,
send_ts: Timestamp,
send_data_kind: SendDataKind,
safety_route: Option<DHTKey>,
remote_private_route: Option<DHTKey>,
reply_private_route: Option<DHTKey>,
safety_route: Option<PublicKey>,
remote_private_route: Option<PublicKey>,
reply_private_route: Option<PublicKey>,
}
/////////////////////////////////////////////////////////////////////
@ -163,38 +176,38 @@ impl<T> Answer<T> {
struct RenderedOperation {
/// The rendered operation bytes
message: Vec<u8>,
/// Destination node id we're sending to
node_id: DHTKey,
/// Node to send envelope to (may not be destination node id in case of relay)
/// Destination node we're sending to
destination_node_ref: NodeRef,
/// Node to send envelope to (may not be destination node in case of relay)
node_ref: NodeRef,
/// Total safety + private route hop count + 1 hop for the initial send
hop_count: usize,
/// The safety route used to send the message
safety_route: Option<DHTKey>,
safety_route: Option<PublicKey>,
/// The private route used to send the message
remote_private_route: Option<DHTKey>,
remote_private_route: Option<PublicKey>,
/// The private route requested to receive the reply
reply_private_route: Option<DHTKey>,
reply_private_route: Option<PublicKey>,
}
/// Node information exchanged during every RPC message
#[derive(Default, Debug, Clone)]
pub struct SenderSignedNodeInfo {
/// The current signed node info of the sender if required
signed_node_info: Option<SignedNodeInfo>,
pub struct SenderPeerInfo {
/// The current peer info of the sender if required
opt_sender_peer_info: Option<PeerInfo>,
/// The last timestamp of the target's node info to assist remote node with sending its latest node info
target_node_info_ts: Timestamp,
}
impl SenderSignedNodeInfo {
pub fn new_no_sni(target_node_info_ts: Timestamp) -> Self {
impl SenderPeerInfo {
pub fn new_no_peer_info(target_node_info_ts: Timestamp) -> Self {
Self {
signed_node_info: None,
opt_sender_peer_info: None,
target_node_info_ts,
}
}
pub fn new(sender_signed_node_info: SignedNodeInfo, target_node_info_ts: Timestamp) -> Self {
pub fn new(sender_peer_info: PeerInfo, target_node_info_ts: Timestamp) -> Self {
Self {
signed_node_info: Some(sender_signed_node_info),
opt_sender_peer_info: Some(sender_peer_info),
target_node_info_ts,
}
}
@ -371,7 +384,7 @@ impl RPCProcessor {
/// If no node was found in the timeout, this returns None
pub async fn search_dht_single_key(
&self,
_node_id: DHTKey,
_node_id: PublicKey,
_count: u32,
_fanout: u32,
_timeout: Option<u64>,
@ -386,7 +399,7 @@ impl RPCProcessor {
/// Search the DHT for the 'count' closest nodes to a key, adding them all to the routing table if they are not there and returning their node references
pub async fn search_dht_multi_key(
&self,
_node_id: DHTKey,
_node_id: PublicKey,
_count: u32,
_fanout: u32,
_timeout: Option<u64>,
@ -399,14 +412,14 @@ impl RPCProcessor {
/// Note: This routine can possible be recursive, hence the SendPinBoxFuture async form
pub fn resolve_node(
&self,
node_id: DHTKey,
node_id: PublicKey,
) -> SendPinBoxFuture<Result<Option<NodeRef>, RPCError>> {
let this = self.clone();
Box::pin(async move {
let routing_table = this.routing_table();
// First see if we have the node in our routing table already
if let Some(nr) = routing_table.lookup_node_ref(node_id) {
if let Some(nr) = routing_table.lookup_any_node_ref(node_id) {
// ensure we have some dial info for the entry already,
// if not, we should do the find_node anyway
if nr.has_any_dial_info() {
@ -429,7 +442,7 @@ impl RPCProcessor {
.await?;
if let Some(nr) = &nr {
if nr.node_id() != node_id {
if nr.node_ids().contains_key(&node_id) {
// found a close node, but not exact within our configured resolve_node timeout
return Ok(None);
}
@ -483,14 +496,20 @@ impl RPCProcessor {
&self,
safety_selection: SafetySelection,
remote_private_route: PrivateRoute,
reply_private_route: Option<DHTKey>,
reply_private_route: Option<PublicKey>,
message_data: Vec<u8>,
) -> Result<NetworkResult<RenderedOperation>, RPCError> {
let routing_table = self.routing_table();
let rss = routing_table.route_spec_store();
// Get useful private route properties
let pr_is_stub = remote_private_route.is_stub();
let pr_hop_count = remote_private_route.hop_count;
let pr_pubkey = remote_private_route.public_key;
let pr_pubkey = remote_private_route.public_key.value;
let crypto_kind = remote_private_route.crypto_kind();
let Some(vcrypto) = self.crypto.get(crypto_kind) else {
return Err(RPCError::internal("crypto not available for selected private route"));
};
// Compile the safety route with the private route
let compiled_route: CompiledRoute = match rss
@ -505,27 +524,21 @@ impl RPCProcessor {
}
};
let sr_is_stub = compiled_route.safety_route.is_stub();
let sr_pubkey = compiled_route.safety_route.public_key;
let sr_pubkey = compiled_route.safety_route.public_key.value;
// Encrypt routed operation
// Xmsg + ENC(Xmsg, DH(PKapr, SKbsr))
// xxx use factory method, get version from somewhere...
let nonce = Crypto::get_random_nonce();
let dh_secret = self
.crypto
let nonce = vcrypto.random_nonce();
let dh_secret = vcrypto
.cached_dh(&pr_pubkey, &compiled_route.secret)
.map_err(RPCError::map_internal("dh failed"))?;
let enc_msg_data = Crypto::encrypt_aead(&message_data, &nonce, &dh_secret, None)
let enc_msg_data = vcrypto
.encrypt_aead(&message_data, &nonce, &dh_secret, None)
.map_err(RPCError::map_internal("encryption failed"))?;
// Make the routed operation
// xxx: replace MAX_CRYPTO_VERSION with the version from the factory
let operation = RoutedOperation::new(
MAX_CRYPTO_VERSION,
safety_selection.get_sequencing(),
nonce,
enc_msg_data,
);
let operation =
RoutedOperation::new(safety_selection.get_sequencing(), nonce, enc_msg_data);
// Prepare route operation
let sr_hop_count = compiled_route.safety_route.hop_count;
@ -533,8 +546,8 @@ impl RPCProcessor {
safety_route: compiled_route.safety_route,
operation,
};
let ssni_route = self
.get_sender_signed_node_info(&Destination::direct(compiled_route.first_hop.clone()))?;
let ssni_route =
self.get_sender_peer_info(&Destination::direct(compiled_route.first_hop.clone()));
let operation = RPCOperation::new_statement(
RPCStatement::new(RPCStatementDetail::Route(route_operation)),
ssni_route,
@ -547,12 +560,11 @@ impl RPCProcessor {
let out_message = builder_to_vec(route_msg)?;
// Get the first hop this is going to
let out_node_id = compiled_route.first_hop.node_id();
let out_hop_count = (1 + sr_hop_count + pr_hop_count) as usize;
let out = RenderedOperation {
message: out_message,
node_id: out_node_id,
destination_node_ref: compiled_route.first_hop.clone(),
node_ref: compiled_route.first_hop,
hop_count: out_hop_count,
safety_route: if sr_is_stub { None } else { Some(sr_pubkey) },
@ -587,7 +599,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),
RespondTo::PrivateRoute(pr) => Some(pr.public_key.value),
},
RPCOperationKind::Statement(_) | RPCOperationKind::Answer(_) => None,
};
@ -607,16 +619,15 @@ impl RPCProcessor {
// --------------------------------------
// Get the actual destination node id accounting for relays
let (node_ref, node_id) = if let Destination::Relay {
let (node_ref, destination_node_ref) = if let Destination::Relay {
relay: _,
target: ref dht_key,
ref target,
safety_selection: _,
} = dest
{
(node_ref.clone(), dht_key.clone())
(node_ref.clone(), target.clone())
} else {
let node_id = node_ref.node_id();
(node_ref.clone(), node_id)
(node_ref.clone(), node_ref.clone())
};
// Handle the existence of safety route
@ -635,7 +646,7 @@ impl RPCProcessor {
// route, we can use a direct envelope instead of routing
out = NetworkResult::value(RenderedOperation {
message,
node_id,
destination_node_ref,
node_ref,
hop_count: 1,
safety_route: None,
@ -646,7 +657,9 @@ impl RPCProcessor {
SafetySelection::Safe(_) => {
// No private route was specified for the request
// but we are using a safety route, so we must create an empty private route
let peer_info = match node_ref.make_peer_info(RoutingDomain::PublicInternet)
// Destination relay is ignored for safety routed operations
let peer_info = match destination_node_ref
.make_peer_info(RoutingDomain::PublicInternet)
{
None => {
return Ok(NetworkResult::no_connection_other(
@ -655,8 +668,10 @@ impl RPCProcessor {
}
Some(pi) => pi,
};
let private_route =
PrivateRoute::new_stub(node_id, RouteNode::PeerInfo(peer_info));
let private_route = PrivateRoute::new_stub(
destination_node_ref.best_node_id(),
RouteNode::PeerInfo(peer_info),
);
// Wrap with safety route
out = self.wrap_with_route(
@ -689,18 +704,17 @@ impl RPCProcessor {
/// Get signed node info to package with RPC messages to improve
/// routing table caching when it is okay to do so
#[instrument(level = "trace", skip(self), ret, err)]
fn get_sender_signed_node_info(
&self,
dest: &Destination,
) -> Result<SenderSignedNodeInfo, RPCError> {
/// Also check target's timestamp of our own node info, to see if we should send that
/// And send our timestamp of the target's node info so they can determine if they should update us on their next rpc
#[instrument(level = "trace", skip(self), ret)]
fn get_sender_peer_info(&self, dest: &Destination) -> SenderPeerInfo {
// Don't do this if the sender is to remain private
// Otherwise we would be attaching the original sender's identity to the final destination,
// thus defeating the purpose of the safety route entirely :P
match dest.get_safety_selection() {
SafetySelection::Unsafe(_) => {}
SafetySelection::Safe(_) => {
return Ok(SenderSignedNodeInfo::default());
return SenderPeerInfo::default();
}
}
@ -715,25 +729,19 @@ impl RPCProcessor {
relay: _,
target,
safety_selection: _,
} => {
if let Some(target) = routing_table.lookup_node_ref(*target) {
target
} else {
// Target was not in our routing table
return Ok(SenderSignedNodeInfo::default());
}
}
} => target.clone(),
Destination::PrivateRoute {
private_route: _,
safety_selection: _,
} => {
return Ok(SenderSignedNodeInfo::default());
return SenderPeerInfo::default();
}
};
let Some(routing_domain) = target.best_routing_domain() else {
// No routing domain for target?
return Err(RPCError::internal(format!("No routing domain for target: {}", target)));
// No routing domain for target, no node info
// Only a stale connection or no connection exists
return SenderPeerInfo::default();
};
// Get the target's node info timestamp
@ -741,7 +749,7 @@ impl RPCProcessor {
// Don't return our node info if it's not valid yet
let Some(own_peer_info) = routing_table.get_own_peer_info(routing_domain) else {
return Ok(SenderSignedNodeInfo::new_no_sni(target_node_info_ts));
return SenderPeerInfo::new_no_peer_info(target_node_info_ts);
};
// Get our node info timestamp
@ -749,13 +757,10 @@ impl RPCProcessor {
// If the target has seen our node info already don't send it again
if target.has_seen_our_node_info_ts(routing_domain, our_node_info_ts) {
return Ok(SenderSignedNodeInfo::new_no_sni(target_node_info_ts));
return SenderPeerInfo::new_no_peer_info(target_node_info_ts);
}
Ok(SenderSignedNodeInfo::new(
own_peer_info.signed_node_info,
target_node_info_ts,
))
SenderPeerInfo::new(own_peer_info, target_node_info_ts)
}
/// Record failure to send to node or route
@ -764,8 +769,8 @@ impl RPCProcessor {
rpc_kind: RPCKind,
send_ts: Timestamp,
node_ref: NodeRef,
safety_route: Option<DHTKey>,
remote_private_route: Option<DHTKey>,
safety_route: Option<PublicKey>,
remote_private_route: Option<PublicKey>,
) {
let wants_answer = matches!(rpc_kind, RPCKind::Question);
@ -793,9 +798,9 @@ impl RPCProcessor {
&self,
send_ts: Timestamp,
node_ref: NodeRef,
safety_route: Option<DHTKey>,
remote_private_route: Option<DHTKey>,
private_route: Option<DHTKey>,
safety_route: Option<PublicKey>,
remote_private_route: Option<PublicKey>,
private_route: Option<PublicKey>,
) {
// Record for node if this was not sent via a route
if safety_route.is_none() && remote_private_route.is_none() {
@ -833,8 +838,8 @@ impl RPCProcessor {
send_ts: Timestamp,
bytes: ByteCount,
node_ref: NodeRef,
safety_route: Option<DHTKey>,
remote_private_route: Option<DHTKey>,
safety_route: Option<PublicKey>,
remote_private_route: Option<PublicKey>,
) {
let wants_answer = matches!(rpc_kind, RPCKind::Question);
@ -870,9 +875,9 @@ impl RPCProcessor {
recv_ts: Timestamp,
bytes: ByteCount,
node_ref: NodeRef,
safety_route: Option<DHTKey>,
remote_private_route: Option<DHTKey>,
reply_private_route: Option<DHTKey>,
safety_route: Option<PublicKey>,
remote_private_route: Option<PublicKey>,
reply_private_route: Option<PublicKey>,
) {
// Record stats for remote node if this was direct
if safety_route.is_none() && remote_private_route.is_none() && reply_private_route.is_none()
@ -999,11 +1004,11 @@ impl RPCProcessor {
dest: Destination,
question: RPCQuestion,
) -> Result<NetworkResult<WaitableReply>, RPCError> {
// Get sender signed node info if we should send that
let ssni = self.get_sender_signed_node_info(&dest)?;
// Get sender peer info if we should send that
let spi = self.get_sender_peer_info(&dest);
// Wrap question in operation
let operation = RPCOperation::new_question(question, ssni);
let operation = RPCOperation::new_question(question, spi);
let op_id = operation.op_id();
// Log rpc send
@ -1012,7 +1017,7 @@ impl RPCProcessor {
// Produce rendered operation
let RenderedOperation {
message,
node_id,
destination_node_ref,
node_ref,
hop_count,
safety_route,
@ -1032,7 +1037,7 @@ impl RPCProcessor {
let send_ts = get_aligned_timestamp();
let send_data_kind = network_result_try!(self
.network_manager()
.send_envelope(node_ref.clone(), Some(node_id), message)
.send_envelope(node_ref.clone(), Some(destination_node_ref), message)
.await
.map_err(|e| {
// If we're returning an error, clean up
@ -1074,11 +1079,11 @@ impl RPCProcessor {
dest: Destination,
statement: RPCStatement,
) -> Result<NetworkResult<()>, RPCError> {
// Get sender signed node info if we should send that
let ssni = self.get_sender_signed_node_info(&dest)?;
// Get sender peer info if we should send that
let spi = self.get_sender_peer_info(&dest);
// Wrap statement in operation
let operation = RPCOperation::new_statement(statement, ssni);
let operation = RPCOperation::new_statement(statement, spi);
// Log rpc send
trace!(target: "rpc_message", dir = "send", kind = "statement", op_id = operation.op_id().as_u64(), desc = operation.kind().desc(), ?dest);
@ -1086,7 +1091,7 @@ impl RPCProcessor {
// Produce rendered operation
let RenderedOperation {
message,
node_id,
destination_node_ref,
node_ref,
hop_count: _,
safety_route,
@ -1099,7 +1104,7 @@ impl RPCProcessor {
let send_ts = get_aligned_timestamp();
let _send_data_kind = network_result_try!(self
.network_manager()
.send_envelope(node_ref.clone(), Some(node_id), message)
.send_envelope(node_ref.clone(), Some(destination_node_ref), message)
.await
.map_err(|e| {
// If we're returning an error, clean up
@ -1136,10 +1141,10 @@ impl RPCProcessor {
let dest = network_result_try!(self.get_respond_to_destination(&request));
// Get sender signed node info if we should send that
let ssni = self.get_sender_signed_node_info(&dest)?;
let spi = self.get_sender_peer_info(&dest);
// Wrap answer in operation
let operation = RPCOperation::new_answer(&request.operation, answer, ssni);
let operation = RPCOperation::new_answer(&request.operation, answer, spi);
// Log rpc send
trace!(target: "rpc_message", dir = "send", kind = "answer", op_id = operation.op_id().as_u64(), desc = operation.kind().desc(), ?dest);
@ -1147,7 +1152,7 @@ impl RPCProcessor {
// Produce rendered operation
let RenderedOperation {
message,
node_id,
destination_node_ref,
node_ref,
hop_count: _,
safety_route,
@ -1159,7 +1164,7 @@ impl RPCProcessor {
let bytes: ByteCount = (message.len() as u64).into();
let send_ts = get_aligned_timestamp();
network_result_try!(self.network_manager()
.send_envelope(node_ref.clone(), Some(node_id), message)
.send_envelope(node_ref.clone(), Some(destination_node_ref), message)
.await
.map_err(|e| {
// If we're returning an error, clean up
@ -1197,7 +1202,10 @@ impl RPCProcessor {
let routing_domain = detail.routing_domain;
// Decode the operation
let sender_node_id = detail.envelope.get_sender_id();
let sender_node_id = TypedKey::new(
detail.envelope.get_crypto_kind(),
detail.envelope.get_sender_id(),
);
// Decode the RPC message
let operation = {
@ -1206,22 +1214,23 @@ impl RPCProcessor {
.get_root::<veilid_capnp::operation::Reader>()
.map_err(RPCError::protocol)
.map_err(logthru_rpc!())?;
RPCOperation::decode(&op_reader, Some(&sender_node_id))?
RPCOperation::decode(&op_reader, self.crypto.clone())?
};
// Get the sender noderef, incorporating and 'sender node info'
// Get the sender noderef, incorporating sender's peer info
let mut opt_sender_nr: Option<NodeRef> = None;
if let Some(sender_node_info) = operation.sender_node_info() {
// Sender NodeInfo was specified, update our routing table with it
if !self.filter_node_info(routing_domain, &sender_node_info) {
if let Some(sender_peer_info) = operation.sender_peer_info() {
// Ensure the sender peer info is for the actual sender specified in the envelope
// Sender PeerInfo was specified, update our routing table with it
if !self.filter_node_info(routing_domain, &sender_peer_info.signed_node_info) {
return Err(RPCError::invalid_format(
"sender signednodeinfo has invalid peer scope",
"sender peerinfo has invalid peer scope",
));
}
opt_sender_nr = self.routing_table().register_node_with_signed_node_info(
opt_sender_nr = self.routing_table().register_node_with_peer_info(
routing_domain,
sender_node_id,
sender_node_info.clone(),
sender_peer_info.clone(),
false,
);
}
@ -1252,7 +1261,7 @@ impl RPCProcessor {
.get_root::<veilid_capnp::operation::Reader>()
.map_err(RPCError::protocol)
.map_err(logthru_rpc!())?;
RPCOperation::decode(&op_reader, None)?
RPCOperation::decode(&op_reader, self.crypto.clone())?
};
// Make the RPC message
@ -1386,15 +1395,17 @@ impl RPCProcessor {
}
#[instrument(level = "trace", skip(self, body), err)]
pub fn enqueue_safety_routed_message(
fn enqueue_safety_routed_message(
&self,
remote_safety_route: DHTKey,
direct: RPCMessageHeaderDetailDirect,
remote_safety_route: PublicKey,
sequencing: Sequencing,
body: Vec<u8>,
) -> EyreResult<()> {
let msg = RPCMessageEncoded {
header: RPCMessageHeader {
detail: RPCMessageHeaderDetail::SafetyRouted(RPCMessageHeaderDetailSafetyRouted {
direct,
remote_safety_route,
sequencing,
}),
@ -1415,10 +1426,11 @@ impl RPCProcessor {
}
#[instrument(level = "trace", skip(self, body), err)]
pub fn enqueue_private_routed_message(
fn enqueue_private_routed_message(
&self,
remote_safety_route: DHTKey,
private_route: DHTKey,
direct: RPCMessageHeaderDetailDirect,
remote_safety_route: PublicKey,
private_route: PublicKey,
safety_spec: SafetySpec,
body: Vec<u8>,
) -> EyreResult<()> {
@ -1426,6 +1438,7 @@ impl RPCProcessor {
header: RPCMessageHeader {
detail: RPCMessageHeaderDetail::PrivateRouted(
RPCMessageHeaderDetailPrivateRouted {
direct,
remote_safety_route,
private_route,
safety_spec,

Some files were not shown because too many files have changed in this diff Show More