mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-10-11 01:58:45 -04:00
Switch to crypto to typed keys everywhere
This commit is contained in:
parent
88f69ce237
commit
848da0ae4e
180 changed files with 8532 additions and 8488 deletions
|
@ -20,6 +20,8 @@
|
|||
- This will only be a breaking change for anyone utilizing the previous `writer` argument.
|
||||
- `writer` is now a member of `SetDHTValueOptions`, alongside the new `allow_offline` property.
|
||||
- Eliminated DHTW capability, merged into DHTV capability, now there is only one DHT enabling/disabling capability and all operations are part of it
|
||||
- Crypto / CryptoSystem functions now use typed keys everywhere (#483)
|
||||
- Eliminated 'best' CryptoKind concept, crypto kinds must now be explicitly stated, otherwise upgrades of veilid-core that change the 'best' CryptoKind could break functionality silently.
|
||||
|
||||
- veilid-core:
|
||||
- Add private route example
|
||||
|
@ -29,6 +31,7 @@
|
|||
- Improved `TypedXXX` conversion traits, including to and from `Vec<u8>`
|
||||
- Ensure utf8 replacement characters are never emitted in logs
|
||||
- Export `CRYPTO_KIND_VLD0` constant
|
||||
- Added SequenceOrdering enum to represent ordering mode for protocols rather than a bool
|
||||
|
||||
- veilid-python:
|
||||
- Correction of type hints
|
||||
|
@ -38,6 +41,10 @@
|
|||
- veilid-server:
|
||||
- Use `detect_address_changes: auto` by default
|
||||
|
||||
- veilid-wasm:
|
||||
- Reorganize crate and add `js` and `dart` features to enable different target bindings
|
||||
- Revamp bindings for `js` to eliminate excessive strings and improve marshaling
|
||||
|
||||
**Changed in Veilid 0.4.7**
|
||||
|
||||
- _BREAKING API CHANGES_:
|
||||
|
|
963
Cargo.lock
generated
963
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -281,7 +281,7 @@ unit-tests-wasm-linux:
|
|||
FROM +code-linux
|
||||
# Just run build now because actual unit tests require network access
|
||||
# which should be moved to a separate integration test
|
||||
RUN veilid-wasm/wasm_build.sh release
|
||||
RUN veilid-wasm/wasm_build_dart.sh release
|
||||
|
||||
unit-tests-linux:
|
||||
WAIT
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
pushd $SCRIPTDIR/.. >/dev/null
|
||||
|
||||
cargo-zigbuild clippy --target x86_64-unknown-linux-gnu
|
||||
cargo-zigbuild clippy --target x86_64-unknown-linux-gnu --manifest-path=veilid-server/Cargo.toml --no-default-features --features=default-async-std
|
||||
cargo-zigbuild clippy --target x86_64-pc-windows-gnu
|
||||
cargo-zigbuild clippy --target aarch64-apple-darwin
|
||||
cargo clippy --manifest-path=veilid-wasm/Cargo.toml --target wasm32-unknown-unknown
|
||||
cargo-zigbuild clippy --target x86_64-unknown-linux-gnu $@
|
||||
cargo-zigbuild clippy --target x86_64-unknown-linux-gnu --manifest-path=veilid-server/Cargo.toml --no-default-features --features=default-async-std $@
|
||||
cargo-zigbuild clippy --target x86_64-pc-windows-gnu $@
|
||||
cargo-zigbuild clippy --target aarch64-apple-darwin $@
|
||||
cargo clippy --manifest-path=veilid-wasm/Cargo.toml --target wasm32-unknown-unknown $@
|
||||
|
||||
popd >/dev/null
|
||||
popd >/dev/null
|
||||
|
|
|
@ -233,6 +233,7 @@ serde_bytes = { version = "0.11", default-features = false, features = [
|
|||
] }
|
||||
tsify = { version = "0.5.5", features = ["js"] }
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
wasm-bindgen-derive = "0.3.0"
|
||||
|
||||
# Network
|
||||
ws_stream_wasm = "0.7.4"
|
||||
|
|
|
@ -184,13 +184,13 @@ async fn create_route(
|
|||
veilid_api_scope(update_callback, config, |veilid_api| async move {
|
||||
|
||||
// Create a new private route endpoint
|
||||
let (route_id, route_blob) =
|
||||
let RouteBlob { route_id, blob } =
|
||||
try_again_loop(|| async { veilid_api.new_private_route().await }).await?;
|
||||
|
||||
// Print the blob
|
||||
println!(
|
||||
"Route id created: {route_id}\nConnect with this private route blob:\ncargo run --example private-route-example -- --connect {}",
|
||||
data_encoding::BASE64.encode(&route_blob)
|
||||
data_encoding::BASE64.encode(&blob)
|
||||
);
|
||||
|
||||
// Wait for enter key to exit the application
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
use super::*;
|
||||
|
||||
pub(crate) const VEILID_DOMAIN_API: &[u8] = b"VEILID_API";
|
||||
|
||||
pub trait CryptoSystem {
|
||||
// Accessors
|
||||
fn kind(&self) -> CryptoKind;
|
||||
fn crypto(&self) -> VeilidComponentGuard<'_, Crypto>;
|
||||
|
||||
// Cached Operations
|
||||
fn cached_dh(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret>;
|
||||
|
||||
// Generation
|
||||
fn random_bytes(&self, len: u32) -> Vec<u8>;
|
||||
fn hash_password(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<String>;
|
||||
fn verify_password(&self, password: &[u8], password_hash: &str) -> VeilidAPIResult<bool>;
|
||||
fn derive_shared_secret(
|
||||
&self,
|
||||
password: &[u8],
|
||||
salt: &[u8],
|
||||
) -> VeilidAPIResult<BareSharedSecret>;
|
||||
fn random_nonce(&self) -> BareNonce;
|
||||
fn random_shared_secret(&self) -> BareSharedSecret;
|
||||
fn compute_dh(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret>;
|
||||
fn generate_shared_secret(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
domain: &[u8],
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
let dh = self.compute_dh(key, secret)?;
|
||||
Ok(BareSharedSecret::from(
|
||||
self.generate_hash(&[&dh, domain, VEILID_DOMAIN_API].concat()),
|
||||
))
|
||||
}
|
||||
fn generate_keypair(&self) -> BareKeyPair;
|
||||
fn generate_hash(&self, data: &[u8]) -> BareHashDigest;
|
||||
fn generate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
) -> VeilidAPIResult<BarePublicKey>;
|
||||
|
||||
// Validation
|
||||
fn shared_secret_length(&self) -> usize;
|
||||
fn nonce_length(&self) -> usize;
|
||||
fn hash_digest_length(&self) -> usize;
|
||||
fn public_key_length(&self) -> usize;
|
||||
fn secret_key_length(&self) -> usize;
|
||||
fn signature_length(&self) -> usize;
|
||||
fn default_salt_length(&self) -> usize;
|
||||
fn aead_overhead(&self) -> usize;
|
||||
|
||||
fn check_shared_secret(&self, secret: &BareSharedSecret) -> VeilidAPIResult<()> {
|
||||
if secret.len() != self.shared_secret_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid shared secret length: {} != {}",
|
||||
secret.len(),
|
||||
self.shared_secret_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_nonce(&self, nonce: &BareNonce) -> VeilidAPIResult<()> {
|
||||
if nonce.len() != self.nonce_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid nonce length: {} != {}",
|
||||
nonce.len(),
|
||||
self.nonce_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_hash_digest(&self, hash: &BareHashDigest) -> VeilidAPIResult<()> {
|
||||
if hash.len() != self.hash_digest_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid hash digest length: {} != {}",
|
||||
hash.len(),
|
||||
self.hash_digest_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_public_key(&self, key: &BarePublicKey) -> VeilidAPIResult<()> {
|
||||
if key.len() != self.public_key_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid public key length: {} != {}",
|
||||
key.len(),
|
||||
self.public_key_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_secret_key(&self, key: &BareSecretKey) -> VeilidAPIResult<()> {
|
||||
if key.len() != self.secret_key_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid secret key length: {} != {}",
|
||||
key.len(),
|
||||
self.secret_key_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_signature(&self, signature: &BareSignature) -> VeilidAPIResult<()> {
|
||||
if signature.len() != self.signature_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid signature length: {} != {}",
|
||||
signature.len(),
|
||||
self.signature_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_keypair(&self, key: &BarePublicKey, secret: &BareSecretKey) -> bool;
|
||||
fn validate_hash(&self, data: &[u8], hash: &BareHashDigest) -> bool;
|
||||
fn validate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
hash: &BareHashDigest,
|
||||
) -> VeilidAPIResult<bool>;
|
||||
|
||||
// Distance Metric
|
||||
fn distance(&self, hash1: &BareHashDigest, hash2: &BareHashDigest) -> BareHashDistance;
|
||||
|
||||
// Authentication
|
||||
fn sign(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
data: &[u8],
|
||||
) -> VeilidAPIResult<BareSignature>;
|
||||
fn verify(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
data: &[u8],
|
||||
signature: &BareSignature,
|
||||
) -> VeilidAPIResult<bool>;
|
||||
|
||||
// AEAD Encrypt/Decrypt
|
||||
fn decrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()>;
|
||||
fn decrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>>;
|
||||
fn encrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()>;
|
||||
fn encrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>>;
|
||||
|
||||
// NoAuth Encrypt/Decrypt
|
||||
fn crypt_in_place_no_auth(
|
||||
&self,
|
||||
body: &mut [u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
) -> VeilidAPIResult<()>;
|
||||
fn crypt_b2b_no_auth(
|
||||
&self,
|
||||
in_buf: &[u8],
|
||||
out_buf: &mut [u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
) -> VeilidAPIResult<()>;
|
||||
fn crypt_no_auth_aligned_8(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>>;
|
||||
fn crypt_no_auth_unaligned(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>>;
|
||||
}
|
213
veilid-core/src/crypto/crypto_system/mod.rs
Normal file
213
veilid-core/src/crypto/crypto_system/mod.rs
Normal file
|
@ -0,0 +1,213 @@
|
|||
use super::*;
|
||||
mod blake3digest512;
|
||||
|
||||
#[cfg(feature = "enable-crypto-none")]
|
||||
pub(crate) mod none;
|
||||
#[cfg(feature = "enable-crypto-vld0")]
|
||||
pub(crate) mod vld0;
|
||||
// #[cfg(feature = "enable-crypto-vld1")]
|
||||
// pub(crate) mod vld1;
|
||||
|
||||
pub(crate) const VEILID_DOMAIN_API: &[u8] = b"VEILID_API";
|
||||
|
||||
#[cfg(feature = "enable-crypto-none")]
|
||||
pub use none::sizes::*;
|
||||
#[cfg(feature = "enable-crypto-none")]
|
||||
pub use none::*;
|
||||
#[cfg(feature = "enable-crypto-vld0")]
|
||||
pub use vld0::sizes::*;
|
||||
#[cfg(feature = "enable-crypto-vld0")]
|
||||
pub use vld0::*;
|
||||
// #[cfg(feature = "enable-crypto-vld1")]
|
||||
// pub use vld1::*;
|
||||
|
||||
pub use blake3digest512::*;
|
||||
|
||||
pub trait CryptoSystem {
|
||||
// Accessors
|
||||
fn kind(&self) -> CryptoKind;
|
||||
fn crypto(&self) -> VeilidComponentGuard<'_, Crypto>;
|
||||
|
||||
// Cached Operations
|
||||
fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret>;
|
||||
|
||||
// Generation
|
||||
fn random_bytes(&self, len: u32) -> Vec<u8>;
|
||||
fn hash_password(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<String>;
|
||||
fn verify_password(&self, password: &[u8], password_hash: &str) -> VeilidAPIResult<bool>;
|
||||
fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<SharedSecret>;
|
||||
fn random_nonce(&self) -> Nonce;
|
||||
fn random_shared_secret(&self) -> SharedSecret;
|
||||
fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret>;
|
||||
fn generate_shared_secret(
|
||||
&self,
|
||||
key: &PublicKey,
|
||||
secret: &SecretKey,
|
||||
domain: &[u8],
|
||||
) -> VeilidAPIResult<SharedSecret> {
|
||||
let dh = self.compute_dh(key, secret)?;
|
||||
let hash = self.generate_hash(&[&dh.into_value(), domain, VEILID_DOMAIN_API].concat());
|
||||
Ok(SharedSecret::new(
|
||||
hash.kind(),
|
||||
BareSharedSecret::new(&hash.into_value()),
|
||||
))
|
||||
}
|
||||
fn generate_keypair(&self) -> KeyPair;
|
||||
fn generate_hash(&self, data: &[u8]) -> HashDigest;
|
||||
fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<PublicKey>;
|
||||
|
||||
// Validation
|
||||
fn shared_secret_length(&self) -> usize;
|
||||
fn nonce_length(&self) -> usize;
|
||||
fn hash_digest_length(&self) -> usize;
|
||||
fn public_key_length(&self) -> usize;
|
||||
fn secret_key_length(&self) -> usize;
|
||||
fn signature_length(&self) -> usize;
|
||||
fn default_salt_length(&self) -> usize;
|
||||
fn aead_overhead(&self) -> usize;
|
||||
|
||||
fn check_shared_secret(&self, secret: &SharedSecret) -> VeilidAPIResult<()> {
|
||||
if secret.kind() != self.kind() {
|
||||
apibail_generic!("incorrect shared secret kind");
|
||||
}
|
||||
if secret.value().len() != self.shared_secret_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid shared secret length: {} != {}",
|
||||
secret.value().len(),
|
||||
self.shared_secret_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_nonce(&self, nonce: &Nonce) -> VeilidAPIResult<()> {
|
||||
if nonce.len() != self.nonce_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid nonce length: {} != {}",
|
||||
nonce.len(),
|
||||
self.nonce_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_hash_digest(&self, hash: &HashDigest) -> VeilidAPIResult<()> {
|
||||
if hash.kind() != self.kind() {
|
||||
apibail_generic!("incorrect hash digest kind");
|
||||
}
|
||||
if hash.value().len() != self.hash_digest_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid hash digest length: {} != {}",
|
||||
hash.value().len(),
|
||||
self.hash_digest_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_public_key(&self, key: &PublicKey) -> VeilidAPIResult<()> {
|
||||
if key.kind() != self.kind() {
|
||||
apibail_generic!("incorrect public key kind");
|
||||
}
|
||||
if key.value().len() != self.public_key_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid public key length: {} != {}",
|
||||
key.value().len(),
|
||||
self.public_key_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_secret_key(&self, key: &SecretKey) -> VeilidAPIResult<()> {
|
||||
if key.kind() != self.kind() {
|
||||
apibail_generic!("incorrect secret key kind");
|
||||
}
|
||||
if key.value().len() != self.secret_key_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid secret key length: {} != {}",
|
||||
key.value().len(),
|
||||
self.secret_key_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn check_signature(&self, signature: &Signature) -> VeilidAPIResult<()> {
|
||||
if signature.kind() != self.kind() {
|
||||
apibail_generic!("incorrect signature kind");
|
||||
}
|
||||
if signature.value().len() != self.signature_length() {
|
||||
apibail_generic!(format!(
|
||||
"invalid signature length: {} != {}",
|
||||
signature.value().len(),
|
||||
self.signature_length()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_keypair(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<bool>;
|
||||
fn validate_hash(&self, data: &[u8], hash: &HashDigest) -> VeilidAPIResult<bool>;
|
||||
fn validate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
hash: &HashDigest,
|
||||
) -> VeilidAPIResult<bool>;
|
||||
|
||||
// Authentication
|
||||
fn sign(&self, key: &PublicKey, secret: &SecretKey, data: &[u8]) -> VeilidAPIResult<Signature>;
|
||||
fn verify(&self, key: &PublicKey, data: &[u8], signature: &Signature) -> VeilidAPIResult<bool>;
|
||||
|
||||
// AEAD Encrypt/Decrypt
|
||||
fn decrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()>;
|
||||
fn decrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>>;
|
||||
fn encrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()>;
|
||||
fn encrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>>;
|
||||
|
||||
// NoAuth Encrypt/Decrypt
|
||||
fn crypt_in_place_no_auth(
|
||||
&self,
|
||||
body: &mut [u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<()>;
|
||||
fn crypt_b2b_no_auth(
|
||||
&self,
|
||||
in_buf: &[u8],
|
||||
out_buf: &mut [u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<()>;
|
||||
fn crypt_no_auth_aligned_8(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>>;
|
||||
fn crypt_no_auth_unaligned(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>>;
|
||||
}
|
|
@ -6,11 +6,11 @@ use data_encoding::BASE64URL_NOPAD;
|
|||
use digest::rand_core::RngCore;
|
||||
use digest::Digest;
|
||||
const NONE_AEAD_OVERHEAD: usize = NONE_PUBLIC_KEY_LENGTH;
|
||||
pub const CRYPTO_KIND_NONE: CryptoKind = CryptoKind(*b"NONE");
|
||||
pub const CRYPTO_KIND_NONE: CryptoKind = CryptoKind::new(*b"NONE");
|
||||
pub const CRYPTO_KIND_NONE_FOURCC: u32 = u32::from_be_bytes(*b"NONE");
|
||||
pub use sizes::*;
|
||||
|
||||
pub fn none_generate_keypair() -> BareKeyPair {
|
||||
pub fn none_generate_keypair() -> KeyPair {
|
||||
let mut csprng = VeilidRng {};
|
||||
let mut pub_bytes = [0u8; NONE_PUBLIC_KEY_LENGTH];
|
||||
let mut sec_bytes = [0u8; NONE_SECRET_KEY_LENGTH];
|
||||
|
@ -20,7 +20,7 @@ pub fn none_generate_keypair() -> BareKeyPair {
|
|||
}
|
||||
let dht_key = BarePublicKey::new(&pub_bytes);
|
||||
let dht_key_secret = BareSecretKey::new(&sec_bytes);
|
||||
BareKeyPair::new(dht_key, dht_key_secret)
|
||||
KeyPair::new(CRYPTO_KIND_NONE, BareKeyPair::new(dht_key, dht_key_secret))
|
||||
}
|
||||
|
||||
fn do_xor_32(a: &[u8], b: &[u8]) -> VeilidAPIResult<[u8; 32]> {
|
||||
|
@ -93,11 +93,7 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
}
|
||||
|
||||
// Cached Operations
|
||||
fn cached_dh(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
|
||||
self.crypto()
|
||||
.cached_dh_internal::<CryptoSystemNONE>(self, key, secret)
|
||||
}
|
||||
|
@ -128,50 +124,51 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
Ok(self.hash_password(password, &salt)? == password_hash)
|
||||
}
|
||||
|
||||
fn derive_shared_secret(
|
||||
&self,
|
||||
password: &[u8],
|
||||
salt: &[u8],
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<SharedSecret> {
|
||||
if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH {
|
||||
apibail_generic!("invalid salt length");
|
||||
}
|
||||
Ok(BareSharedSecret::new(
|
||||
blake3::hash(self.hash_password(password, salt)?.as_bytes()).as_bytes(),
|
||||
Ok(SharedSecret::new(
|
||||
CRYPTO_KIND_NONE,
|
||||
BareSharedSecret::new(
|
||||
blake3::hash(self.hash_password(password, salt)?.as_bytes()).as_bytes(),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn random_nonce(&self) -> BareNonce {
|
||||
fn random_nonce(&self) -> Nonce {
|
||||
let mut nonce = [0u8; NONE_NONCE_LENGTH];
|
||||
random_bytes(&mut nonce);
|
||||
BareNonce::new(&nonce)
|
||||
Nonce::new(&nonce)
|
||||
}
|
||||
fn random_shared_secret(&self) -> BareSharedSecret {
|
||||
fn random_shared_secret(&self) -> SharedSecret {
|
||||
let mut s = [0u8; NONE_SHARED_SECRET_LENGTH];
|
||||
random_bytes(&mut s);
|
||||
BareSharedSecret::new(&s)
|
||||
SharedSecret::new(CRYPTO_KIND_NONE, BareSharedSecret::new(&s))
|
||||
}
|
||||
fn compute_dh(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
let s = do_xor_32(key, secret)?;
|
||||
Ok(BareSharedSecret::new(&s))
|
||||
fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
|
||||
let s = do_xor_32(key.ref_value(), secret.ref_value())?;
|
||||
Ok(SharedSecret::new(
|
||||
CRYPTO_KIND_NONE,
|
||||
BareSharedSecret::new(&s),
|
||||
))
|
||||
}
|
||||
fn generate_keypair(&self) -> BareKeyPair {
|
||||
fn generate_keypair(&self) -> KeyPair {
|
||||
none_generate_keypair()
|
||||
}
|
||||
fn generate_hash(&self, data: &[u8]) -> BareHashDigest {
|
||||
BareHashDigest::new(blake3::hash(data).as_bytes())
|
||||
fn generate_hash(&self, data: &[u8]) -> HashDigest {
|
||||
HashDigest::new(
|
||||
CRYPTO_KIND_NONE,
|
||||
BareHashDigest::new(blake3::hash(data).as_bytes()),
|
||||
)
|
||||
}
|
||||
fn generate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
) -> VeilidAPIResult<BarePublicKey> {
|
||||
fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<PublicKey> {
|
||||
let mut hasher = blake3::Hasher::new();
|
||||
std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
|
||||
Ok(BarePublicKey::new(hasher.finalize().as_bytes()))
|
||||
Ok(PublicKey::new(
|
||||
CRYPTO_KIND_NONE,
|
||||
BarePublicKey::new(hasher.finalize().as_bytes()),
|
||||
))
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
@ -200,49 +197,56 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
NONE_SIGNATURE_LENGTH
|
||||
}
|
||||
|
||||
fn validate_keypair(&self, dht_key: &BarePublicKey, dht_key_secret: &BareSecretKey) -> bool {
|
||||
fn validate_keypair(
|
||||
&self,
|
||||
public_key: &PublicKey,
|
||||
secret_key: &SecretKey,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
self.check_public_key(public_key)?;
|
||||
self.check_secret_key(secret_key)?;
|
||||
|
||||
let data = vec![0u8; 512];
|
||||
let Ok(sig) = self.sign(dht_key, dht_key_secret, &data) else {
|
||||
return false;
|
||||
let Ok(sig) = self.sign(public_key, secret_key, &data) else {
|
||||
return Ok(false);
|
||||
};
|
||||
let Ok(v) = self.verify(dht_key, &data, &sig) else {
|
||||
return false;
|
||||
let Ok(v) = self.verify(public_key, &data, &sig) else {
|
||||
return Ok(false);
|
||||
};
|
||||
v
|
||||
Ok(v)
|
||||
}
|
||||
fn validate_hash(&self, data: &[u8], dht_key: &BareHashDigest) -> bool {
|
||||
let bytes = *blake3::hash(data).as_bytes();
|
||||
bytes == dht_key.bytes()
|
||||
fn validate_hash(&self, data: &[u8], hash_digest: &HashDigest) -> VeilidAPIResult<bool> {
|
||||
self.check_hash_digest(hash_digest)?;
|
||||
let out_hash = blake3::hash(data);
|
||||
let bytes = out_hash.as_bytes();
|
||||
Ok(*bytes == **hash_digest.ref_value())
|
||||
}
|
||||
fn validate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
dht_key: &BareHashDigest,
|
||||
hash_digest: &HashDigest,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
self.check_hash_digest(hash_digest)?;
|
||||
let mut hasher = blake3::Hasher::new();
|
||||
std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
|
||||
let bytes = *hasher.finalize().as_bytes();
|
||||
Ok(bytes == dht_key.bytes())
|
||||
}
|
||||
// Distance Metric
|
||||
fn distance(&self, key1: &BareHashDigest, key2: &BareHashDigest) -> BareHashDistance {
|
||||
let mut bytes = [0u8; NONE_HASH_DIGEST_LENGTH];
|
||||
|
||||
for (n, byte) in bytes.iter_mut().enumerate() {
|
||||
*byte = key1[n] ^ key2[n];
|
||||
}
|
||||
|
||||
BareHashDistance::new(&bytes)
|
||||
let out_hash = hasher.finalize();
|
||||
let bytes = out_hash.as_bytes();
|
||||
Ok(*bytes == **hash_digest.ref_value())
|
||||
}
|
||||
|
||||
// Authentication
|
||||
fn sign(
|
||||
&self,
|
||||
dht_key: &BarePublicKey,
|
||||
dht_key_secret: &BareSecretKey,
|
||||
public_key: &PublicKey,
|
||||
secret_key: &SecretKey,
|
||||
data: &[u8],
|
||||
) -> VeilidAPIResult<BareSignature> {
|
||||
if !is_bytes_eq_32(&do_xor_32(dht_key, dht_key_secret)?, 0xFFu8)? {
|
||||
) -> VeilidAPIResult<Signature> {
|
||||
self.check_public_key(public_key)?;
|
||||
self.check_secret_key(secret_key)?;
|
||||
|
||||
if !is_bytes_eq_32(
|
||||
&do_xor_32(public_key.ref_value(), secret_key.ref_value())?,
|
||||
0xFFu8,
|
||||
)? {
|
||||
return Err(VeilidAPIError::parse_error(
|
||||
"Keypair is invalid",
|
||||
"invalid keys",
|
||||
|
@ -255,31 +259,43 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
let in_sig_bytes: [u8; NONE_SIGNATURE_LENGTH] = sig.into();
|
||||
let mut sig_bytes = [0u8; NONE_SIGNATURE_LENGTH];
|
||||
sig_bytes[0..32].copy_from_slice(&in_sig_bytes[0..32]);
|
||||
sig_bytes[32..64].copy_from_slice(&do_xor_32(&in_sig_bytes[32..64], dht_key_secret)?);
|
||||
let dht_sig = BareSignature::new(&sig_bytes);
|
||||
sig_bytes[32..64]
|
||||
.copy_from_slice(&do_xor_32(&in_sig_bytes[32..64], secret_key.ref_value())?);
|
||||
let dht_sig = Signature::new(CRYPTO_KIND_NONE, BareSignature::new(&sig_bytes));
|
||||
println!("DEBUG dht_sig: {:?}", dht_sig);
|
||||
Ok(dht_sig)
|
||||
}
|
||||
|
||||
fn verify(
|
||||
&self,
|
||||
dht_key: &BarePublicKey,
|
||||
public_key: &PublicKey,
|
||||
data: &[u8],
|
||||
signature: &BareSignature,
|
||||
signature: &Signature,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
self.check_public_key(public_key)?;
|
||||
self.check_signature(signature)?;
|
||||
|
||||
let mut dig = Blake3Digest512::new();
|
||||
dig.update(data);
|
||||
let sig = dig.finalize();
|
||||
let in_sig_bytes: [u8; NONE_SIGNATURE_LENGTH] = sig.into();
|
||||
let mut verify_bytes = [0u8; NONE_SIGNATURE_LENGTH];
|
||||
verify_bytes[0..32].copy_from_slice(&do_xor_32(&in_sig_bytes[0..32], &signature[0..32])?);
|
||||
verify_bytes[32..64]
|
||||
.copy_from_slice(&do_xor_32(&in_sig_bytes[32..64], &signature[32..64])?);
|
||||
verify_bytes[0..32].copy_from_slice(&do_xor_32(
|
||||
&in_sig_bytes[0..32],
|
||||
&signature.ref_value()[0..32],
|
||||
)?);
|
||||
verify_bytes[32..64].copy_from_slice(&do_xor_32(
|
||||
&in_sig_bytes[32..64],
|
||||
&signature.ref_value()[32..64],
|
||||
)?);
|
||||
|
||||
if !is_bytes_eq_32(&verify_bytes[0..32], 0u8)? {
|
||||
return Ok(false);
|
||||
}
|
||||
if !is_bytes_eq_32(&do_xor_32(&verify_bytes[32..64], dht_key)?, 0xFFu8)? {
|
||||
if !is_bytes_eq_32(
|
||||
&do_xor_32(&verify_bytes[32..64], public_key.ref_value())?,
|
||||
0xFFu8,
|
||||
)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
|
@ -290,13 +306,16 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
fn decrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
_associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut blob = nonce.to_vec();
|
||||
blob.extend_from_slice(&[0u8; 8]);
|
||||
let blob = do_xor_32(&blob, shared_secret)?;
|
||||
let blob = do_xor_32(&blob, shared_secret.ref_value())?;
|
||||
|
||||
if body.len() < NONE_AEAD_OVERHEAD {
|
||||
return Err(VeilidAPIError::generic("invalid length"));
|
||||
|
@ -311,10 +330,13 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
fn decrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut out = body.to_vec();
|
||||
self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
|
||||
.map_err(map_to_string)
|
||||
|
@ -325,13 +347,16 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
fn encrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
_associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut blob = nonce.to_vec();
|
||||
blob.extend_from_slice(&[0u8; 8]);
|
||||
let blob = do_xor_32(&blob, shared_secret)?;
|
||||
let blob = do_xor_32(&blob, shared_secret.ref_value())?;
|
||||
do_xor_inplace(body, &blob)?;
|
||||
body.append(&mut blob.to_vec());
|
||||
Ok(())
|
||||
|
@ -340,10 +365,13 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
fn encrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut out = body.to_vec();
|
||||
self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
|
||||
.map_err(map_to_string)
|
||||
|
@ -355,12 +383,15 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
fn crypt_in_place_no_auth(
|
||||
&self,
|
||||
body: &mut [u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<()> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut blob = nonce.to_vec();
|
||||
blob.extend_from_slice(&[0u8; 8]);
|
||||
let blob = do_xor_32(&blob, shared_secret)?;
|
||||
let blob = do_xor_32(&blob, shared_secret.ref_value())?;
|
||||
do_xor_inplace(body, &blob)
|
||||
}
|
||||
|
||||
|
@ -368,21 +399,27 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
&self,
|
||||
in_buf: &[u8],
|
||||
out_buf: &mut [u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<()> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut blob = nonce.to_vec();
|
||||
blob.extend_from_slice(&[0u8; 8]);
|
||||
let blob = do_xor_32(&blob, shared_secret)?;
|
||||
let blob = do_xor_32(&blob, shared_secret.ref_value())?;
|
||||
do_xor_b2b(in_buf, out_buf, &blob)
|
||||
}
|
||||
|
||||
fn crypt_no_auth_aligned_8(
|
||||
&self,
|
||||
in_buf: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) };
|
||||
self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?;
|
||||
Ok(out_buf)
|
||||
|
@ -391,9 +428,12 @@ impl CryptoSystem for CryptoSystemNONE {
|
|||
fn crypt_no_auth_unaligned(
|
||||
&self,
|
||||
in_buf: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) };
|
||||
self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?;
|
||||
Ok(out_buf)
|
|
@ -19,13 +19,14 @@ const VLD0_DOMAIN_SIGN: &[u8] = b"VLD0_SIGN";
|
|||
const VLD0_DOMAIN_CRYPT: &[u8] = b"VLD0_CRYPT";
|
||||
|
||||
const VLD0_AEAD_OVERHEAD: usize = 16;
|
||||
pub const CRYPTO_KIND_VLD0: CryptoKind = CryptoKind(*b"VLD0");
|
||||
pub const CRYPTO_KIND_VLD0: CryptoKind = CryptoKind::new(*b"VLD0");
|
||||
pub const CRYPTO_KIND_VLD0_FOURCC: u32 = u32::from_be_bytes(*b"VLD0");
|
||||
pub use sizes::*;
|
||||
|
||||
fn public_to_x25519_pk(public: &BarePublicKey) -> VeilidAPIResult<xd::PublicKey> {
|
||||
fn public_to_x25519_pk(public: &PublicKey) -> VeilidAPIResult<xd::PublicKey> {
|
||||
let pk_ed = ed::VerifyingKey::from_bytes(
|
||||
public
|
||||
.ref_value()
|
||||
.bytes()
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
|
@ -33,11 +34,11 @@ fn public_to_x25519_pk(public: &BarePublicKey) -> VeilidAPIResult<xd::PublicKey>
|
|||
.map_err(VeilidAPIError::internal)?;
|
||||
Ok(xd::PublicKey::from(*pk_ed.to_montgomery().as_bytes()))
|
||||
}
|
||||
fn secret_to_x25519_sk(secret: &BareSecretKey) -> VeilidAPIResult<xd::StaticSecret> {
|
||||
fn secret_to_x25519_sk(secret: &SecretKey) -> VeilidAPIResult<xd::StaticSecret> {
|
||||
// NOTE: ed::SigningKey.to_scalar() does not produce an unreduced scalar, we want the raw bytes here
|
||||
// See https://github.com/dalek-cryptography/curve25519-dalek/issues/565
|
||||
let hash: [u8; VLD0_SIGNATURE_LENGTH] = ed::Sha512::default()
|
||||
.chain_update(secret.bytes())
|
||||
.chain_update(secret.ref_value().bytes())
|
||||
.finalize()
|
||||
.into();
|
||||
let mut output = [0u8; VLD0_SECRET_KEY_LENGTH];
|
||||
|
@ -46,14 +47,14 @@ fn secret_to_x25519_sk(secret: &BareSecretKey) -> VeilidAPIResult<xd::StaticSecr
|
|||
Ok(xd::StaticSecret::from(output))
|
||||
}
|
||||
|
||||
pub(crate) fn vld0_generate_keypair() -> BareKeyPair {
|
||||
pub(crate) fn vld0_generate_keypair() -> KeyPair {
|
||||
let mut csprng = VeilidRng {};
|
||||
let signing_key = ed::SigningKey::generate(&mut csprng);
|
||||
let verifying_key = signing_key.verifying_key();
|
||||
let public_key = BarePublicKey::new(&verifying_key.to_bytes());
|
||||
let secret_key = BareSecretKey::new(&signing_key.to_bytes());
|
||||
|
||||
BareKeyPair::new(public_key, secret_key)
|
||||
KeyPair::new(CRYPTO_KIND_VLD0, BareKeyPair::new(public_key, secret_key))
|
||||
}
|
||||
|
||||
/// V0 CryptoSystem
|
||||
|
@ -80,11 +81,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
|
||||
// Cached Operations
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
fn cached_dh(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
|
||||
self.crypto()
|
||||
.cached_dh_internal::<CryptoSystemVLD0>(self, key, secret)
|
||||
}
|
||||
|
@ -125,11 +122,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn derive_shared_secret(
|
||||
&self,
|
||||
password: &[u8],
|
||||
salt: &[u8],
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<SharedSecret> {
|
||||
if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH {
|
||||
apibail_generic!("invalid salt length");
|
||||
}
|
||||
|
@ -141,29 +134,28 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
argon2
|
||||
.hash_password_into(password, salt, &mut output_key_material)
|
||||
.map_err(VeilidAPIError::generic)?;
|
||||
Ok(BareSharedSecret::new(&output_key_material))
|
||||
Ok(SharedSecret::new(
|
||||
CRYPTO_KIND_VLD0,
|
||||
BareSharedSecret::new(&output_key_material),
|
||||
))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn random_nonce(&self) -> BareNonce {
|
||||
fn random_nonce(&self) -> Nonce {
|
||||
let mut nonce = [0u8; VLD0_NONCE_LENGTH];
|
||||
random_bytes(&mut nonce);
|
||||
BareNonce::new(&nonce)
|
||||
Nonce::new(&nonce)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn random_shared_secret(&self) -> BareSharedSecret {
|
||||
fn random_shared_secret(&self) -> SharedSecret {
|
||||
let mut s = [0u8; VLD0_SHARED_SECRET_LENGTH];
|
||||
random_bytes(&mut s);
|
||||
BareSharedSecret::new(&s)
|
||||
SharedSecret::new(CRYPTO_KIND_VLD0, BareSharedSecret::new(&s))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn compute_dh(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
|
||||
let pk_xd = public_to_x25519_pk(key)?;
|
||||
let sk_xd = secret_to_x25519_sk(secret)?;
|
||||
|
||||
|
@ -174,27 +166,33 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
hasher.update(&dh_bytes);
|
||||
let output = hasher.finalize();
|
||||
|
||||
Ok(BareSharedSecret::new(output.as_bytes()))
|
||||
Ok(SharedSecret::new(
|
||||
CRYPTO_KIND_VLD0,
|
||||
BareSharedSecret::new(output.as_bytes()),
|
||||
))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn generate_keypair(&self) -> BareKeyPair {
|
||||
fn generate_keypair(&self) -> KeyPair {
|
||||
vld0_generate_keypair()
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn generate_hash(&self, data: &[u8]) -> BareHashDigest {
|
||||
BareHashDigest::new(blake3::hash(data).as_bytes())
|
||||
fn generate_hash(&self, data: &[u8]) -> HashDigest {
|
||||
HashDigest::new(
|
||||
CRYPTO_KIND_VLD0,
|
||||
BareHashDigest::new(blake3::hash(data).as_bytes()),
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn generate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
) -> VeilidAPIResult<BarePublicKey> {
|
||||
fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<PublicKey> {
|
||||
let mut hasher = blake3::Hasher::new();
|
||||
std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
|
||||
Ok(BarePublicKey::new(hasher.finalize().as_bytes()))
|
||||
Ok(PublicKey::new(
|
||||
CRYPTO_KIND_VLD0,
|
||||
BarePublicKey::new(hasher.finalize().as_bytes()),
|
||||
))
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
@ -224,61 +222,63 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn validate_keypair(&self, public_key: &BarePublicKey, secret_key: &BareSecretKey) -> bool {
|
||||
fn validate_keypair(
|
||||
&self,
|
||||
public_key: &PublicKey,
|
||||
secret_key: &SecretKey,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
self.check_public_key(public_key)?;
|
||||
self.check_secret_key(secret_key)?;
|
||||
|
||||
let data = vec![0u8; 512];
|
||||
let Ok(sig) = self.sign(public_key, secret_key, &data) else {
|
||||
return false;
|
||||
return Ok(false);
|
||||
};
|
||||
let Ok(v) = self.verify(public_key, &data, &sig) else {
|
||||
return false;
|
||||
return Ok(false);
|
||||
};
|
||||
v
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn validate_hash(&self, data: &[u8], hash_digest: &BareHashDigest) -> bool {
|
||||
fn validate_hash(&self, data: &[u8], hash_digest: &HashDigest) -> VeilidAPIResult<bool> {
|
||||
self.check_hash_digest(hash_digest)?;
|
||||
|
||||
let bytes = *blake3::hash(data).as_bytes();
|
||||
|
||||
bytes == hash_digest.bytes()
|
||||
Ok(bytes == hash_digest.ref_value().bytes())
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn validate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
hash_digest: &BareHashDigest,
|
||||
hash_digest: &HashDigest,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
self.check_hash_digest(hash_digest)?;
|
||||
|
||||
let mut hasher = blake3::Hasher::new();
|
||||
std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
|
||||
let bytes = *hasher.finalize().as_bytes();
|
||||
Ok(bytes == hash_digest.bytes())
|
||||
}
|
||||
|
||||
// Distance Metric
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn distance(&self, hash1: &BareHashDigest, hash2: &BareHashDigest) -> BareHashDistance {
|
||||
let mut bytes = [0u8; VLD0_HASH_DIGEST_LENGTH];
|
||||
|
||||
(0..VLD0_HASH_DIGEST_LENGTH).for_each(|n| {
|
||||
bytes[n] = hash1[n] ^ hash2[n];
|
||||
});
|
||||
|
||||
BareHashDistance::new(&bytes)
|
||||
Ok(bytes == hash_digest.ref_value().bytes())
|
||||
}
|
||||
|
||||
// Authentication
|
||||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn sign(
|
||||
&self,
|
||||
public_key: &BarePublicKey,
|
||||
secret_key: &BareSecretKey,
|
||||
public_key: &PublicKey,
|
||||
secret_key: &SecretKey,
|
||||
data: &[u8],
|
||||
) -> VeilidAPIResult<BareSignature> {
|
||||
) -> VeilidAPIResult<Signature> {
|
||||
self.check_public_key(public_key)?;
|
||||
self.check_secret_key(secret_key)?;
|
||||
|
||||
let mut kpb: [u8; VLD0_SECRET_KEY_LENGTH + VLD0_PUBLIC_KEY_LENGTH] =
|
||||
[0u8; VLD0_SECRET_KEY_LENGTH + VLD0_PUBLIC_KEY_LENGTH];
|
||||
|
||||
kpb[..VLD0_SECRET_KEY_LENGTH].copy_from_slice(secret_key);
|
||||
kpb[VLD0_SECRET_KEY_LENGTH..].copy_from_slice(public_key);
|
||||
kpb[..VLD0_SECRET_KEY_LENGTH].copy_from_slice(secret_key.ref_value().bytes());
|
||||
kpb[VLD0_SECRET_KEY_LENGTH..].copy_from_slice(public_key.ref_value().bytes());
|
||||
let keypair = ed::SigningKey::from_keypair_bytes(&kpb)
|
||||
.map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?;
|
||||
|
||||
|
@ -289,7 +289,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
.sign_prehashed(dig, Some(VLD0_DOMAIN_SIGN))
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
||||
let sig = BareSignature::new(&sig_bytes.to_bytes());
|
||||
let sig = Signature::new(CRYPTO_KIND_VLD0, BareSignature::new(&sig_bytes.to_bytes()));
|
||||
|
||||
if !self.verify(public_key, data, &sig)? {
|
||||
apibail_internal!("newly created signature does not verify");
|
||||
|
@ -300,12 +300,16 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
#[instrument(level = "trace", target = "crypto", skip_all)]
|
||||
fn verify(
|
||||
&self,
|
||||
public_key: &BarePublicKey,
|
||||
public_key: &PublicKey,
|
||||
data: &[u8],
|
||||
signature: &BareSignature,
|
||||
signature: &Signature,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
self.check_public_key(public_key)?;
|
||||
self.check_signature(signature)?;
|
||||
|
||||
let pk = ed::VerifyingKey::from_bytes(
|
||||
public_key
|
||||
.ref_value()
|
||||
.bytes()
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
|
@ -313,6 +317,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
.map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?;
|
||||
let sig = ed::Signature::from_bytes(
|
||||
signature
|
||||
.ref_value()
|
||||
.bytes()
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
|
@ -335,11 +340,14 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
fn decrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()> {
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret
|
||||
.ref_value()
|
||||
.bytes()
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
@ -358,10 +366,13 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
fn decrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut out = body.to_vec();
|
||||
self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
|
||||
.map_err(map_to_string)
|
||||
|
@ -373,11 +384,15 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
fn encrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret
|
||||
.ref_value()
|
||||
.bytes()
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
@ -397,10 +412,13 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
fn encrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut out = body.to_vec();
|
||||
self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
|
||||
.map_err(map_to_string)
|
||||
|
@ -413,10 +431,14 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
fn crypt_in_place_no_auth(
|
||||
&self,
|
||||
body: &mut [u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<()> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret
|
||||
.ref_value()
|
||||
.bytes()
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
@ -435,10 +457,14 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
&self,
|
||||
in_buf: &[u8],
|
||||
out_buf: &mut [u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<()> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let shared_secret_bytes: [u8; VLD0_SHARED_SECRET_LENGTH] = shared_secret
|
||||
.ref_value()
|
||||
.bytes()
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
@ -458,9 +484,12 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
fn crypt_no_auth_aligned_8(
|
||||
&self,
|
||||
in_buf: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) };
|
||||
self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?;
|
||||
Ok(out_buf)
|
||||
|
@ -470,9 +499,12 @@ impl CryptoSystem for CryptoSystemVLD0 {
|
|||
fn crypt_no_auth_unaligned(
|
||||
&self,
|
||||
in_buf: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
self.check_nonce(nonce)?;
|
||||
self.check_shared_secret(shared_secret)?;
|
||||
|
||||
let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) };
|
||||
self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret)?;
|
||||
Ok(out_buf)
|
|
@ -1,4 +1,4 @@
|
|||
use super::*;
|
||||
|
||||
pub const CRYPTO_KIND_VLD1: CryptoKind = CryptoKind(*b"VLD1");
|
||||
pub const CRYPTO_KIND_VLD1: CryptoKind = CryptoKind::new(*b"VLD1");
|
||||
pub const CRYPTO_KIND_VLD1_FOURCC: u32 = u32::from_be_bytes(*b"VLD1");
|
|
@ -1,45 +1,62 @@
|
|||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
// Diffie-Hellman key exchange cache
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
// Diffie-Hellman key agreement cache
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct DHCacheKey {
|
||||
pub key: BarePublicKey,
|
||||
pub secret: BareSecretKey,
|
||||
pub key: PublicKey,
|
||||
pub secret: SecretKey,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
impl fmt::Display for DHCacheKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}.{}", self.key, self.secret)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for DHCacheKey {
|
||||
type Err = VeilidAPIError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let Some((pks, sks)) = s.split_once('.') else {
|
||||
apibail_parse_error!("s", s);
|
||||
};
|
||||
let key = PublicKey::from_str(pks)?;
|
||||
let secret = SecretKey::from_str(sks)?;
|
||||
Ok(DHCacheKey { key, secret })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for DHCacheKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
FromStr::from_str(&s).map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for DHCacheKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DHCacheValue {
|
||||
pub shared_secret: BareSharedSecret,
|
||||
pub shared_secret: SharedSecret,
|
||||
}
|
||||
|
||||
pub type DHCache = LruCache<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_from_slice(&e.0.key);
|
||||
out.extend_from_slice(&e.0.secret);
|
||||
out.extend_from_slice(&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
|
||||
serialize_json_bytes(cache)
|
||||
}
|
||||
|
||||
pub fn bytes_to_cache(bytes: &[u8], cache: &mut DHCache) {
|
||||
for d in bytes.chunks(32 + 32 + 32) {
|
||||
let k = DHCacheKey {
|
||||
key: BarePublicKey::new(d[0..32].try_into().expect("asdf")),
|
||||
secret: BareSecretKey::new(d[32..64].try_into().expect("asdf")),
|
||||
};
|
||||
let v = DHCacheValue {
|
||||
shared_secret: BareSharedSecret::new(d[64..96].try_into().expect("asdf")),
|
||||
};
|
||||
cache.insert(k, v);
|
||||
}
|
||||
pub fn bytes_to_cache(bytes: &[u8]) -> EyreResult<DHCache> {
|
||||
deserialize_json_bytes(bytes).wrap_err("cache format invalid")
|
||||
}
|
||||
|
|
|
@ -1,364 +0,0 @@
|
|||
#![allow(clippy::absurd_extreme_comparisons)]
|
||||
use super::*;
|
||||
use crate::*;
|
||||
use core::convert::TryInto;
|
||||
|
||||
pub const MAX_ENVELOPE_SIZE: usize = 65507;
|
||||
pub const MIN_ENVELOPE_SIZE: usize = 0x6A + 0x40; // Header + BareSignature
|
||||
|
||||
// Version number of envelope format
|
||||
fourcc_type!(EnvelopeVersion);
|
||||
|
||||
pub const ENVELOPE_VERSION_VLD0: EnvelopeVersion = EnvelopeVersion(*b"ENV0");
|
||||
// pub const ENVELOPE_VERSION_VLD0_FOURCC: u32 = u32::from_be_bytes(*b"ENV0");
|
||||
|
||||
/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one
|
||||
pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [ENVELOPE_VERSION_VLD0];
|
||||
/// Number of envelope versions to keep on structures if many are present beyond the ones we consider valid
|
||||
pub const MAX_ENVELOPE_VERSIONS: usize = 16;
|
||||
|
||||
/// Envelopes are versioned
|
||||
///
|
||||
/// These are the formats for the on-the-wire serialization performed by this module
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct EnvelopeHeader {
|
||||
/// // Size is at least 4 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
|
||||
/// version: [u8; 4], // 0x00: 0x45 0x4E 0x56 0x30 ("ENV0")
|
||||
/// }
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct EnvelopeV0 {
|
||||
/// // Size is 106 bytes without signature and 170 with signature
|
||||
/// version: [u8; 4], // 0x00: 0x45 0x4E 0x56 0x30 ("ENV0")
|
||||
/// crypto_kind: [u8; 4], // 0x04: CryptoSystemVersion FOURCC code (CryptoKind)
|
||||
/// size: u16, // 0x08: Total size of the envelope including the encrypted operations message. Maximum size is 65,507 bytes, which is the data size limit for a single UDP message on IPv4.
|
||||
/// timestamp: u64, // 0x0A: Duration since UNIX_EPOCH in microseconds when this message is sent. Messages older than 10 seconds are dropped.
|
||||
/// nonce: [u8; 24], // 0x12: Random nonce for replay protection and for dh
|
||||
/// sender_id: [u8; 32], // 0x2A: Node ID of the message source, which is the public key of the sender (must be verified with find_node if this is a new node_id/address combination)
|
||||
/// recipient_id: [u8; 32], // 0x4A: Node ID of the intended recipient, which is the public key of the recipient (must be the receiving node, or a relay lease holder)
|
||||
/// // 0x6A: message is appended (operations)
|
||||
/// signature: [u8; 64], // 0x?? (end-0x40): BareSignature of the entire envelope including header is appended to the packet
|
||||
/// // entire header needs to be included in message digest, relays are not allowed to modify the envelope without invalidating the signature.
|
||||
/// }
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Envelope {
|
||||
version: EnvelopeVersion,
|
||||
crypto_kind: CryptoKind,
|
||||
timestamp: Timestamp,
|
||||
nonce: BareNonce,
|
||||
sender_id: BareNodeId,
|
||||
recipient_id: BareNodeId,
|
||||
}
|
||||
|
||||
impl Envelope {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
version: EnvelopeVersion,
|
||||
crypto_kind: CryptoKind,
|
||||
timestamp: Timestamp,
|
||||
nonce: BareNonce,
|
||||
sender_id: BareNodeId,
|
||||
recipient_id: BareNodeId,
|
||||
) -> Self {
|
||||
assert!(VALID_ENVELOPE_VERSIONS.contains(&version));
|
||||
assert!(VALID_CRYPTO_KINDS.contains(&crypto_kind));
|
||||
Self {
|
||||
version,
|
||||
crypto_kind,
|
||||
timestamp,
|
||||
nonce,
|
||||
sender_id,
|
||||
recipient_id,
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "envelope", skip_all)]
|
||||
pub fn from_signed_data(
|
||||
crypto: &Crypto,
|
||||
data: &[u8],
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Envelope> {
|
||||
// Ensure we are at least the length of the envelope
|
||||
// Silent drop here, as we use zero length packets as part of the protocol for hole punching
|
||||
if data.len() < MIN_ENVELOPE_SIZE {
|
||||
apibail_generic!("envelope data too small");
|
||||
}
|
||||
|
||||
// Check envelope version
|
||||
let version: EnvelopeVersion = data[0x00..0x04]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
if !VALID_ENVELOPE_VERSIONS.contains(&version) {
|
||||
apibail_parse_error!("unsupported envelope version", version);
|
||||
}
|
||||
|
||||
// Check crypto kind
|
||||
let crypto_kind = CryptoKind(
|
||||
data[0x04..0x08]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
let Some(vcrypto) = crypto.get(crypto_kind) else {
|
||||
apibail_parse_error!("unsupported crypto kind", crypto_kind);
|
||||
};
|
||||
|
||||
// Get size and ensure it matches the size of the envelope and is less than the maximum message size
|
||||
let size: u16 = u16::from_le_bytes(
|
||||
data[0x08..0x0A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
if (size as usize) > MAX_ENVELOPE_SIZE {
|
||||
apibail_parse_error!("envelope too large", size);
|
||||
}
|
||||
if (size as usize) != data.len() {
|
||||
apibail_parse_error!(
|
||||
"size doesn't match envelope size",
|
||||
format!(
|
||||
"size doesn't match envelope size: size={} data.len()={}",
|
||||
size,
|
||||
data.len()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Get the timestamp
|
||||
let timestamp: Timestamp = u64::from_le_bytes(
|
||||
data[0x0A..0x12]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
)
|
||||
.into();
|
||||
|
||||
// Get nonce and sender node id
|
||||
let mut nonce_slice: [u8; VLD0_NONCE_LENGTH] = data[0x12..0x2A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
let mut sender_id_slice: [u8; VLD0_HASH_DIGEST_LENGTH] = data[0x2A..0x4A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
let mut recipient_id_slice: [u8; VLD0_HASH_DIGEST_LENGTH] = data[0x4A..0x6A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
||||
// Apply network key (not the best, but it will keep networks from colliding without much overhead)
|
||||
if let Some(nk) = network_key.as_ref() {
|
||||
for n in 0..VLD0_NONCE_LENGTH {
|
||||
nonce_slice[n] ^= nk[n];
|
||||
}
|
||||
for n in 0..VLD0_HASH_DIGEST_LENGTH {
|
||||
sender_id_slice[n] ^= nk[n];
|
||||
}
|
||||
for n in 0..VLD0_HASH_DIGEST_LENGTH {
|
||||
recipient_id_slice[n] ^= nk[n];
|
||||
}
|
||||
}
|
||||
|
||||
let nonce: BareNonce = BareNonce::new(&nonce_slice);
|
||||
let sender_id = BareNodeId::new(&sender_id_slice);
|
||||
let recipient_id = BareNodeId::new(&recipient_id_slice);
|
||||
|
||||
// Ensure sender_id and recipient_id are not the same
|
||||
if sender_id == recipient_id {
|
||||
apibail_parse_error!(
|
||||
"sender_id should not be same as recipient_id",
|
||||
recipient_id.encode()
|
||||
);
|
||||
}
|
||||
|
||||
// Get signature
|
||||
let signature = BareSignature::new(
|
||||
data[(data.len() - 64)..]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
|
||||
// Validate signature
|
||||
if !vcrypto
|
||||
.verify(
|
||||
&sender_id.clone().into(),
|
||||
&data[0..(data.len() - 64)],
|
||||
&signature,
|
||||
)
|
||||
.map_err(VeilidAPIError::internal)?
|
||||
{
|
||||
apibail_parse_error!("signature verification of envelope failed", signature);
|
||||
}
|
||||
|
||||
// Return envelope
|
||||
Ok(Self {
|
||||
version,
|
||||
crypto_kind,
|
||||
timestamp,
|
||||
nonce,
|
||||
sender_id,
|
||||
recipient_id,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "envelope", skip_all)]
|
||||
pub fn decrypt_body(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
data: &[u8],
|
||||
secret_key: &BareSecretKey,
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
// Get DH secret
|
||||
let vcrypto = crypto
|
||||
.get(self.crypto_kind)
|
||||
.expect("need to ensure only valid crypto kinds here");
|
||||
let mut dh_secret = vcrypto.cached_dh(&self.sender_id.clone().into(), secret_key)?;
|
||||
|
||||
// Apply network key
|
||||
if let Some(nk) = network_key.as_ref() {
|
||||
let mut dh_secret_bytes = dh_secret.to_vec();
|
||||
|
||||
for n in 0..VLD0_SHARED_SECRET_LENGTH {
|
||||
dh_secret_bytes[n] ^= nk[n];
|
||||
}
|
||||
|
||||
dh_secret = BareSharedSecret::new(&dh_secret_bytes);
|
||||
}
|
||||
// Decrypt message without authentication
|
||||
let body = vcrypto.crypt_no_auth_aligned_8(
|
||||
&data[0x6A..data.len() - 64],
|
||||
&self.nonce,
|
||||
&dh_secret,
|
||||
)?;
|
||||
|
||||
// Decompress body
|
||||
let body = decompress_size_prepended(&body, Some(MAX_ENVELOPE_SIZE))?;
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "envelope", skip_all, err)]
|
||||
pub fn to_encrypted_data(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
body: &[u8],
|
||||
secret_key: &BareSecretKey,
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
// Ensure body isn't too long
|
||||
let uncompressed_body_size: usize = body.len() + MIN_ENVELOPE_SIZE;
|
||||
if uncompressed_body_size > MAX_ENVELOPE_SIZE {
|
||||
apibail_parse_error!(
|
||||
"envelope size before compression is too large",
|
||||
uncompressed_body_size
|
||||
);
|
||||
}
|
||||
|
||||
// Compress body
|
||||
let body = compress_prepend_size(body);
|
||||
|
||||
// Ensure body isn't too long
|
||||
let envelope_size: usize = body.len() + MIN_ENVELOPE_SIZE;
|
||||
if envelope_size > MAX_ENVELOPE_SIZE {
|
||||
apibail_parse_error!(
|
||||
"envelope size after compression is too large",
|
||||
envelope_size
|
||||
);
|
||||
}
|
||||
// Generate dh secret
|
||||
let vcrypto = crypto
|
||||
.get(self.crypto_kind)
|
||||
.expect("need to ensure only valid crypto kinds here");
|
||||
let mut dh_secret = vcrypto.cached_dh(&self.recipient_id.clone().into(), secret_key)?;
|
||||
|
||||
// Write envelope body
|
||||
let mut data = vec![0u8; envelope_size];
|
||||
|
||||
// Write version
|
||||
data[0x00..0x04].copy_from_slice(&self.version.0);
|
||||
// Write crypto kind
|
||||
data[0x04..0x08].copy_from_slice(&self.crypto_kind.0);
|
||||
// Write size
|
||||
data[0x08..0x0A].copy_from_slice(&(envelope_size as u16).to_le_bytes());
|
||||
// Write timestamp
|
||||
data[0x0A..0x12].copy_from_slice(&self.timestamp.as_u64().to_le_bytes());
|
||||
// Write nonce
|
||||
data[0x12..0x2A].copy_from_slice(&self.nonce);
|
||||
// Write sender node id
|
||||
data[0x2A..0x4A].copy_from_slice(&self.sender_id);
|
||||
// Write recipient node id
|
||||
data[0x4A..0x6A].copy_from_slice(&self.recipient_id);
|
||||
|
||||
// Apply network key (not the best, but it will keep networks from colliding without much overhead)
|
||||
if let Some(nk) = network_key.as_ref() {
|
||||
let mut dh_secret_bytes = dh_secret.to_vec();
|
||||
|
||||
for n in 0..VLD0_SHARED_SECRET_LENGTH {
|
||||
dh_secret_bytes[n] ^= nk[n];
|
||||
}
|
||||
for n in 0..VLD0_NONCE_LENGTH {
|
||||
data[0x12 + n] ^= nk[n];
|
||||
}
|
||||
for n in 0..VLD0_HASH_DIGEST_LENGTH {
|
||||
data[0x2A + n] ^= nk[n];
|
||||
}
|
||||
for n in 0..VLD0_HASH_DIGEST_LENGTH {
|
||||
data[0x4A + n] ^= nk[n];
|
||||
}
|
||||
|
||||
dh_secret = BareSharedSecret::new(&dh_secret_bytes);
|
||||
}
|
||||
|
||||
// Encrypt message
|
||||
let encrypted_body = vcrypto.crypt_no_auth_unaligned(&body, &self.nonce, &dh_secret)?;
|
||||
|
||||
// Write body
|
||||
if !encrypted_body.is_empty() {
|
||||
data[0x6A..envelope_size - 64].copy_from_slice(encrypted_body.as_slice());
|
||||
}
|
||||
|
||||
// Sign the envelope
|
||||
let signature = vcrypto.sign(
|
||||
&self.sender_id.clone().into(),
|
||||
secret_key,
|
||||
&data[0..(envelope_size - 64)],
|
||||
)?;
|
||||
|
||||
// Append the signature
|
||||
data[(envelope_size - 64)..].copy_from_slice(&signature);
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> EnvelopeVersion {
|
||||
self.version
|
||||
}
|
||||
|
||||
pub fn get_crypto_kind(&self) -> CryptoKind {
|
||||
self.crypto_kind
|
||||
}
|
||||
|
||||
pub fn get_timestamp(&self) -> Timestamp {
|
||||
self.timestamp
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_nonce(&self) -> BareNonce {
|
||||
self.nonce.clone()
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_bare_sender_id(&self) -> BareNodeId {
|
||||
self.sender_id.clone()
|
||||
}
|
||||
|
||||
pub fn get_sender_id(&self) -> NodeId {
|
||||
NodeId::new(self.crypto_kind, self.sender_id.clone())
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_bare_recipient_id(&self) -> BareNodeId {
|
||||
self.recipient_id.clone()
|
||||
}
|
||||
|
||||
pub fn get_recipient_id(&self) -> NodeId {
|
||||
NodeId::new(self.crypto_kind, self.recipient_id.clone())
|
||||
}
|
||||
}
|
497
veilid-core/src/crypto/envelope/mod.rs
Normal file
497
veilid-core/src/crypto/envelope/mod.rs
Normal file
|
@ -0,0 +1,497 @@
|
|||
use super::*;
|
||||
use crate::routing_table::*;
|
||||
use core::convert::TryInto;
|
||||
|
||||
// Version number of envelope format
|
||||
fourcc_type!(EnvelopeVersion);
|
||||
|
||||
// ENV0
|
||||
pub const ENVELOPE_VERSION_ENV0: EnvelopeVersion = EnvelopeVersion::new(*b"ENV0");
|
||||
pub const ENV0_NONCE_LENGTH: usize = 24;
|
||||
pub const ENV0_SIGNATURE_LENGTH: usize = 64;
|
||||
pub const ENV0_MAX_ENVELOPE_SIZE: usize = 65507;
|
||||
pub const ENV0_MIN_ENVELOPE_SIZE: usize = 0x6A + 0x40; // Header + BareSignature
|
||||
|
||||
/// Envelope versions in order of preference, best envelope version is the first one, worst is the last one
|
||||
pub const VALID_ENVELOPE_VERSIONS: [EnvelopeVersion; 1] = [ENVELOPE_VERSION_ENV0];
|
||||
/// Number of envelope versions to keep on structures if many are present beyond the ones we consider valid
|
||||
pub const MAX_ENVELOPE_VERSIONS: usize = 16;
|
||||
|
||||
/// Envelopes are versioned
|
||||
///
|
||||
/// These are the formats for the on-the-wire serialization performed by this module
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct EnvelopeHeader {
|
||||
/// // Size is at least 4 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
|
||||
/// version: [u8; 4], // 0x00: 0x45 0x4E 0x56 0x30 ("ENV0")
|
||||
/// }
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct EnvelopeENV0 {
|
||||
/// // Size is 106 bytes without signature and 170 with signature
|
||||
/// version: [u8; 4], // 0x00: 0x45 0x4E 0x56 0x30 ("ENV0")
|
||||
/// crypto_kind: [u8; 4], // 0x04: CryptoSystemVersion FOURCC code (CryptoKind)
|
||||
/// size: u16, // 0x08: Total size of the envelope including the encrypted operations message. Maximum size is 65,507 bytes, which is the data size limit for a single UDP message on IPv4.
|
||||
/// timestamp: u64, // 0x0A: Duration since UNIX_EPOCH in microseconds when this message is sent. Messages older than 10 seconds are dropped.
|
||||
/// nonce: [u8; 24], // 0x12: Random nonce for replay protection and for dh
|
||||
/// sender_id: [u8; 32], // 0x2A: Node ID of the message source, which is the public key of the sender (must be verified with find_node if this is a new node_id/address combination)
|
||||
/// recipient_id: [u8; 32], // 0x4A: Node ID of the intended recipient, which is the public key of the recipient (must be the receiving node, or a relay lease holder)
|
||||
/// // 0x6A: message is appended (operations)
|
||||
/// signature: [u8; 64], // 0x?? (end-0x40): BareSignature of the entire envelope including header is appended to the packet
|
||||
/// // entire header needs to be included in message digest, relays are not allowed to modify the envelope without invalidating the signature.
|
||||
/// }
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Envelope {
|
||||
ENV0 { env0: EnvelopeENV0 },
|
||||
}
|
||||
|
||||
impl Envelope {
|
||||
#[instrument(level = "trace", target = "envelope", skip_all)]
|
||||
pub fn try_new_env0(
|
||||
crypto: &Crypto,
|
||||
crypto_kind: CryptoKind,
|
||||
timestamp: Timestamp,
|
||||
nonce: Nonce,
|
||||
sender_id: NodeId,
|
||||
recipient_id: NodeId,
|
||||
) -> VeilidAPIResult<Self> {
|
||||
Ok(Self::ENV0 {
|
||||
env0: EnvelopeENV0::try_new(
|
||||
crypto,
|
||||
crypto_kind,
|
||||
timestamp,
|
||||
nonce,
|
||||
sender_id,
|
||||
recipient_id,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "envelope", skip_all)]
|
||||
pub fn try_from_signed_data(
|
||||
crypto: &Crypto,
|
||||
data: &[u8],
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Self> {
|
||||
// Ensure we are at least the length of the envelope
|
||||
// Silent drop here, as we use zero length packets as part of the protocol for hole punching
|
||||
if data.len() < 4 {
|
||||
apibail_generic!("envelope header too small");
|
||||
}
|
||||
|
||||
// Check envelope version
|
||||
let version: EnvelopeVersion = data[0x00..0x04]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
||||
match version {
|
||||
ENVELOPE_VERSION_ENV0 => Ok(Self::ENV0 {
|
||||
env0: EnvelopeENV0::try_from_signed_data(crypto, data, network_key)?,
|
||||
}),
|
||||
_ => {
|
||||
apibail_parse_error!("unsupported envelope version", version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "envelope", skip_all)]
|
||||
pub fn decrypt_body(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
data: &[u8],
|
||||
secret_key: &SecretKey,
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
match self {
|
||||
Envelope::ENV0 { env0 } => env0.decrypt_body(crypto, data, secret_key, network_key),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "envelope", skip_all, err)]
|
||||
pub fn to_encrypted_data(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
body: &[u8],
|
||||
secret_key: &SecretKey,
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
match self {
|
||||
Envelope::ENV0 { env0 } => {
|
||||
env0.to_encrypted_data(crypto, body, secret_key, network_key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> EnvelopeVersion {
|
||||
match self {
|
||||
Envelope::ENV0 { env0: _ } => ENVELOPE_VERSION_ENV0,
|
||||
}
|
||||
}
|
||||
pub fn get_crypto_kind(&self) -> CryptoKind {
|
||||
match self {
|
||||
Envelope::ENV0 { env0 } => env0.get_crypto_kind(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_timestamp(&self) -> Timestamp {
|
||||
match self {
|
||||
Envelope::ENV0 { env0 } => env0.get_timestamp(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_sender_id(&self) -> NodeId {
|
||||
match self {
|
||||
Envelope::ENV0 { env0 } => env0.get_sender_id(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_recipient_id(&self) -> NodeId {
|
||||
match self {
|
||||
Envelope::ENV0 { env0 } => env0.get_recipient_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnvelopeENV0 {
|
||||
crypto_kind: CryptoKind,
|
||||
timestamp: Timestamp,
|
||||
nonce: Nonce,
|
||||
bare_sender_id: BareNodeId,
|
||||
bare_recipient_id: BareNodeId,
|
||||
}
|
||||
|
||||
impl EnvelopeENV0 {
|
||||
fn try_new(
|
||||
crypto: &Crypto,
|
||||
crypto_kind: CryptoKind,
|
||||
timestamp: Timestamp,
|
||||
nonce: Nonce,
|
||||
sender_id: NodeId,
|
||||
recipient_id: NodeId,
|
||||
) -> VeilidAPIResult<Self> {
|
||||
let vcrypto = Self::validate_crypto_kind(crypto, crypto_kind)?;
|
||||
|
||||
vcrypto.check_nonce(&nonce)?;
|
||||
Self::check_node_id(crypto_kind, &sender_id)?;
|
||||
Self::check_node_id(crypto_kind, &recipient_id)?;
|
||||
|
||||
Ok(Self {
|
||||
crypto_kind,
|
||||
timestamp,
|
||||
nonce,
|
||||
bare_sender_id: sender_id.value(),
|
||||
bare_recipient_id: recipient_id.value(),
|
||||
})
|
||||
}
|
||||
|
||||
fn try_from_signed_data(
|
||||
crypto: &Crypto,
|
||||
data: &[u8],
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Self> {
|
||||
// Ensure we are at least the length of the envelope
|
||||
// Silent drop here, as we use zero length packets as part of the protocol for hole punching
|
||||
if data.len() < ENV0_MIN_ENVELOPE_SIZE {
|
||||
apibail_generic!("envelope data too small");
|
||||
}
|
||||
|
||||
// Check crypto kind
|
||||
let crypto_kind = CryptoKind::new(
|
||||
data[0x04..0x08]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
|
||||
let vcrypto = Self::validate_crypto_kind(crypto, crypto_kind)?;
|
||||
|
||||
// Get size and ensure it matches the size of the envelope and is less than the maximum message size
|
||||
let size: u16 = u16::from_le_bytes(
|
||||
data[0x08..0x0A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
if (size as usize) > ENV0_MAX_ENVELOPE_SIZE {
|
||||
apibail_parse_error!("envelope too large", size);
|
||||
}
|
||||
if (size as usize) != data.len() {
|
||||
apibail_parse_error!(
|
||||
"size doesn't match envelope size",
|
||||
format!(
|
||||
"size doesn't match envelope size: size={} data.len()={}",
|
||||
size,
|
||||
data.len()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Get the timestamp
|
||||
let timestamp: Timestamp = u64::from_le_bytes(
|
||||
data[0x0A..0x12]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
)
|
||||
.into();
|
||||
|
||||
// Get nonce and sender node id
|
||||
let mut nonce_slice: [u8; ENV0_NONCE_LENGTH] = data[0x12..0x2A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
let mut sender_id_slice: [u8; HASH_COORDINATE_LENGTH] = data[0x2A..0x4A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
let mut recipient_id_slice: [u8; HASH_COORDINATE_LENGTH] = data[0x4A..0x6A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
||||
// Apply network key (not the best, but it will keep networks from colliding without much overhead)
|
||||
if let Some(nk) = network_key.as_ref() {
|
||||
for n in 0..ENV0_NONCE_LENGTH {
|
||||
nonce_slice[n] ^= nk[n];
|
||||
}
|
||||
for n in 0..HASH_COORDINATE_LENGTH {
|
||||
sender_id_slice[n] ^= nk[n];
|
||||
}
|
||||
for n in 0..HASH_COORDINATE_LENGTH {
|
||||
recipient_id_slice[n] ^= nk[n];
|
||||
}
|
||||
}
|
||||
|
||||
let nonce: Nonce = Nonce::new(&nonce_slice);
|
||||
let bare_sender_id = BareNodeId::new(&sender_id_slice);
|
||||
let bare_recipient_id = BareNodeId::new(&recipient_id_slice);
|
||||
|
||||
// Ensure sender_id and recipient_id are not the same
|
||||
if bare_sender_id == bare_recipient_id {
|
||||
apibail_parse_error!(
|
||||
"bare_sender_id should not be same as bare_recipient_id",
|
||||
bare_recipient_id.encode()
|
||||
);
|
||||
}
|
||||
|
||||
let sender_public_key = PublicKey::new(crypto_kind, BarePublicKey::new(&bare_sender_id));
|
||||
|
||||
// Get signature
|
||||
let bare_signature = BareSignature::new(
|
||||
data[(data.len() - ENV0_SIGNATURE_LENGTH)..]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
let signature = Signature::new(crypto_kind, bare_signature);
|
||||
// Validate signature
|
||||
if !vcrypto
|
||||
.verify(
|
||||
&sender_public_key,
|
||||
&data[0..(data.len() - ENV0_SIGNATURE_LENGTH)],
|
||||
&signature,
|
||||
)
|
||||
.map_err(VeilidAPIError::internal)?
|
||||
{
|
||||
apibail_parse_error!("signature verification of envelope failed", signature);
|
||||
}
|
||||
|
||||
// Return envelope
|
||||
Ok(Self {
|
||||
crypto_kind,
|
||||
timestamp,
|
||||
nonce,
|
||||
bare_sender_id,
|
||||
bare_recipient_id,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn decrypt_body(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
data: &[u8],
|
||||
secret_key: &SecretKey,
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
// Get DH secret
|
||||
let vcrypto = crypto
|
||||
.get(self.crypto_kind)
|
||||
.expect("need to ensure only valid crypto kinds here");
|
||||
vcrypto.check_secret_key(secret_key)?;
|
||||
|
||||
let sender_public_key =
|
||||
PublicKey::new(self.crypto_kind, BarePublicKey::new(&self.bare_sender_id));
|
||||
|
||||
let mut dh_secret = vcrypto.cached_dh(&sender_public_key, secret_key)?;
|
||||
|
||||
// Apply network key
|
||||
if let Some(nk) = network_key.as_ref() {
|
||||
let mut dh_secret_bytes = dh_secret.ref_value().to_vec();
|
||||
|
||||
for n in 0..dh_secret_bytes.len() {
|
||||
dh_secret_bytes[n] ^= nk[n % dh_secret_bytes.len()];
|
||||
}
|
||||
|
||||
dh_secret =
|
||||
SharedSecret::new(dh_secret.kind(), BareSharedSecret::new(&dh_secret_bytes));
|
||||
}
|
||||
// Decrypt message without authentication
|
||||
let body = vcrypto.crypt_no_auth_aligned_8(
|
||||
&data[0x6A..data.len() - ENV0_SIGNATURE_LENGTH],
|
||||
&self.nonce,
|
||||
&dh_secret,
|
||||
)?;
|
||||
|
||||
// Decompress body
|
||||
let body = decompress_size_prepended(&body, Some(ENV0_MAX_ENVELOPE_SIZE))?;
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
pub fn to_encrypted_data(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
body: &[u8],
|
||||
secret_key: &SecretKey,
|
||||
network_key: &Option<BareSharedSecret>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
let vcrypto = crypto
|
||||
.get(self.crypto_kind)
|
||||
.expect("need to ensure only valid crypto kinds here");
|
||||
vcrypto.check_secret_key(secret_key)?;
|
||||
|
||||
// Ensure body isn't too long
|
||||
let uncompressed_body_size: usize = body.len() + ENV0_MIN_ENVELOPE_SIZE;
|
||||
if uncompressed_body_size > ENV0_MAX_ENVELOPE_SIZE {
|
||||
apibail_parse_error!(
|
||||
"envelope size before compression is too large",
|
||||
uncompressed_body_size
|
||||
);
|
||||
}
|
||||
|
||||
// Compress body
|
||||
let body = compress_prepend_size(body);
|
||||
|
||||
// Ensure body isn't too long
|
||||
let envelope_size: usize = body.len() + ENV0_MIN_ENVELOPE_SIZE;
|
||||
if envelope_size > ENV0_MAX_ENVELOPE_SIZE {
|
||||
apibail_parse_error!(
|
||||
"envelope size after compression is too large",
|
||||
envelope_size
|
||||
);
|
||||
}
|
||||
// Generate dh secret
|
||||
let recipient_public_key = PublicKey::new(
|
||||
self.crypto_kind,
|
||||
BarePublicKey::new(&self.bare_recipient_id),
|
||||
);
|
||||
|
||||
let mut dh_secret = vcrypto.cached_dh(&recipient_public_key, secret_key)?;
|
||||
|
||||
// Write envelope body
|
||||
let mut data = vec![0u8; envelope_size];
|
||||
|
||||
// Write version
|
||||
data[0x00..0x04].copy_from_slice(&ENVELOPE_VERSION_ENV0.0);
|
||||
// Write crypto kind
|
||||
data[0x04..0x08].copy_from_slice(self.crypto_kind.bytes());
|
||||
// Write size
|
||||
data[0x08..0x0A].copy_from_slice(&(envelope_size as u16).to_le_bytes());
|
||||
// Write timestamp
|
||||
data[0x0A..0x12].copy_from_slice(&self.timestamp.as_u64().to_le_bytes());
|
||||
// Write nonce
|
||||
data[0x12..0x2A].copy_from_slice(&self.nonce);
|
||||
// Write sender node id
|
||||
data[0x2A..0x4A].copy_from_slice(&self.bare_sender_id);
|
||||
// Write recipient node id
|
||||
data[0x4A..0x6A].copy_from_slice(&self.bare_recipient_id);
|
||||
|
||||
// Apply network key (not the best, but it will keep networks from colliding without much overhead)
|
||||
if let Some(nk) = network_key.as_ref() {
|
||||
let mut dh_secret_bytes = dh_secret.ref_value().to_vec();
|
||||
|
||||
for n in 0..dh_secret_bytes.len() {
|
||||
dh_secret_bytes[n] ^= nk[n % dh_secret_bytes.len()];
|
||||
}
|
||||
for n in 0..ENV0_NONCE_LENGTH {
|
||||
data[0x12 + n] ^= nk[n];
|
||||
}
|
||||
for n in 0..HASH_COORDINATE_LENGTH {
|
||||
data[0x2A + n] ^= nk[n];
|
||||
}
|
||||
for n in 0..HASH_COORDINATE_LENGTH {
|
||||
data[0x4A + n] ^= nk[n];
|
||||
}
|
||||
|
||||
dh_secret =
|
||||
SharedSecret::new(dh_secret.kind(), BareSharedSecret::new(&dh_secret_bytes));
|
||||
}
|
||||
|
||||
// Encrypt message
|
||||
let encrypted_body = vcrypto.crypt_no_auth_unaligned(&body, &self.nonce, &dh_secret)?;
|
||||
|
||||
// Write body
|
||||
if !encrypted_body.is_empty() {
|
||||
data[0x6A..envelope_size - ENV0_SIGNATURE_LENGTH]
|
||||
.copy_from_slice(encrypted_body.as_slice());
|
||||
}
|
||||
|
||||
// Sign the envelope
|
||||
let sender_public_key =
|
||||
PublicKey::new(self.crypto_kind, BarePublicKey::new(&self.bare_sender_id));
|
||||
|
||||
let signature = vcrypto.sign(
|
||||
&sender_public_key,
|
||||
secret_key,
|
||||
&data[0..(envelope_size - vcrypto.signature_length())],
|
||||
)?;
|
||||
|
||||
// Append the signature
|
||||
data[(envelope_size - ENV0_SIGNATURE_LENGTH)..].copy_from_slice(signature.ref_value());
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn get_crypto_kind(&self) -> CryptoKind {
|
||||
self.crypto_kind
|
||||
}
|
||||
|
||||
pub fn get_timestamp(&self) -> Timestamp {
|
||||
self.timestamp
|
||||
}
|
||||
|
||||
pub fn get_sender_id(&self) -> NodeId {
|
||||
NodeId::new(self.crypto_kind, self.bare_sender_id.clone())
|
||||
}
|
||||
|
||||
pub fn get_recipient_id(&self) -> NodeId {
|
||||
NodeId::new(self.crypto_kind, self.bare_recipient_id.clone())
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
fn validate_crypto_kind(
|
||||
crypto: &Crypto,
|
||||
crypto_kind: CryptoKind,
|
||||
) -> VeilidAPIResult<CryptoSystemGuard<'_>> {
|
||||
let vcrypto = crypto
|
||||
.get(crypto_kind)
|
||||
.ok_or_else(|| VeilidAPIError::parse_error("unsupported crypto kind", crypto_kind))?;
|
||||
|
||||
// Verify crypto kind can be used with this envelope
|
||||
if vcrypto.nonce_length() != ENV0_NONCE_LENGTH
|
||||
|| vcrypto.hash_digest_length() != HASH_COORDINATE_LENGTH
|
||||
|| vcrypto.public_key_length() != HASH_COORDINATE_LENGTH
|
||||
|| vcrypto.signature_length() != ENV0_SIGNATURE_LENGTH
|
||||
{
|
||||
apibail_generic!("unsupported crypto kind for this envelope type");
|
||||
}
|
||||
|
||||
Ok(vcrypto)
|
||||
}
|
||||
|
||||
fn check_node_id(crypto_kind: CryptoKind, node_id: &NodeId) -> VeilidAPIResult<()> {
|
||||
if node_id.kind() != crypto_kind {
|
||||
apibail_parse_error!("invalid crypto kind for ENV0", node_id.kind());
|
||||
}
|
||||
if node_id.ref_value().len() != HASH_COORDINATE_LENGTH {
|
||||
apibail_parse_error!("invalid node_id length for ENV0", node_id.ref_value().len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use core::marker::PhantomData;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Guard to access a particular cryptosystem
|
||||
|
@ -52,9 +54,9 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
// Cached Operations
|
||||
pub async fn cached_dh(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
key: &PublicKey,
|
||||
secret: &SecretKey,
|
||||
) -> VeilidAPIResult<SharedSecret> {
|
||||
yielding(|| self.guard.cached_dh(key, secret)).await
|
||||
}
|
||||
|
||||
|
@ -77,47 +79,50 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
&self,
|
||||
password: &[u8],
|
||||
salt: &[u8],
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
) -> VeilidAPIResult<SharedSecret> {
|
||||
yielding(|| self.guard.derive_shared_secret(password, salt)).await
|
||||
}
|
||||
pub async fn random_nonce(&self) -> BareNonce {
|
||||
pub async fn random_nonce(&self) -> Nonce {
|
||||
yielding(|| self.guard.random_nonce()).await
|
||||
}
|
||||
pub async fn random_shared_secret(&self) -> BareSharedSecret {
|
||||
pub async fn random_shared_secret(&self) -> SharedSecret {
|
||||
yielding(|| self.guard.random_shared_secret()).await
|
||||
}
|
||||
pub async fn compute_dh(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
key: &PublicKey,
|
||||
secret: &SecretKey,
|
||||
) -> VeilidAPIResult<SharedSecret> {
|
||||
yielding(|| self.guard.compute_dh(key, secret)).await
|
||||
}
|
||||
pub async fn generate_shared_secret(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
key: &PublicKey,
|
||||
secret: &SecretKey,
|
||||
domain: &[u8],
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
) -> VeilidAPIResult<SharedSecret> {
|
||||
let dh = self.compute_dh(key, secret).await?;
|
||||
Ok(BareSharedSecret::from(
|
||||
self.generate_hash(&[&dh, domain, VEILID_DOMAIN_API].concat())
|
||||
.await,
|
||||
let hash = self
|
||||
.generate_hash(&[&dh.into_value(), domain, VEILID_DOMAIN_API].concat())
|
||||
.await;
|
||||
Ok(SharedSecret::new(
|
||||
hash.kind(),
|
||||
BareSharedSecret::new(&hash.into_value()),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn generate_keypair(&self) -> BareKeyPair {
|
||||
pub async fn generate_keypair(&self) -> KeyPair {
|
||||
yielding(|| self.guard.generate_keypair()).await
|
||||
}
|
||||
|
||||
pub async fn generate_hash(&self, data: &[u8]) -> BareHashDigest {
|
||||
pub async fn generate_hash(&self, data: &[u8]) -> HashDigest {
|
||||
yielding(|| self.guard.generate_hash(data)).await
|
||||
}
|
||||
|
||||
pub async fn generate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
) -> VeilidAPIResult<BarePublicKey> {
|
||||
) -> VeilidAPIResult<PublicKey> {
|
||||
yielding(|| self.guard.generate_hash_reader(reader)).await
|
||||
}
|
||||
|
||||
|
@ -154,59 +159,58 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
pub fn default_salt_length(&self) -> usize {
|
||||
self.guard.default_salt_length()
|
||||
}
|
||||
pub fn check_shared_secret(&self, secret: &BareSharedSecret) -> VeilidAPIResult<()> {
|
||||
pub fn check_shared_secret(&self, secret: &SharedSecret) -> VeilidAPIResult<()> {
|
||||
self.guard.check_shared_secret(secret)
|
||||
}
|
||||
pub fn check_nonce(&self, nonce: &BareNonce) -> VeilidAPIResult<()> {
|
||||
pub fn check_nonce(&self, nonce: &Nonce) -> VeilidAPIResult<()> {
|
||||
self.guard.check_nonce(nonce)
|
||||
}
|
||||
pub fn check_hash_digest(&self, hash: &BareHashDigest) -> VeilidAPIResult<()> {
|
||||
pub fn check_hash_digest(&self, hash: &HashDigest) -> VeilidAPIResult<()> {
|
||||
self.guard.check_hash_digest(hash)
|
||||
}
|
||||
pub fn check_public_key(&self, key: &BarePublicKey) -> VeilidAPIResult<()> {
|
||||
pub fn check_public_key(&self, key: &PublicKey) -> VeilidAPIResult<()> {
|
||||
self.guard.check_public_key(key)
|
||||
}
|
||||
pub fn check_secret_key(&self, key: &BareSecretKey) -> VeilidAPIResult<()> {
|
||||
pub fn check_secret_key(&self, key: &SecretKey) -> VeilidAPIResult<()> {
|
||||
self.guard.check_secret_key(key)
|
||||
}
|
||||
pub fn check_signature(&self, signature: &BareSignature) -> VeilidAPIResult<()> {
|
||||
pub fn check_signature(&self, signature: &Signature) -> VeilidAPIResult<()> {
|
||||
self.guard.check_signature(signature)
|
||||
}
|
||||
pub async fn validate_keypair(&self, key: &BarePublicKey, secret: &BareSecretKey) -> bool {
|
||||
pub async fn validate_keypair(
|
||||
&self,
|
||||
key: &PublicKey,
|
||||
secret: &SecretKey,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
yielding(|| self.guard.validate_keypair(key, secret)).await
|
||||
}
|
||||
|
||||
pub async fn validate_hash(&self, data: &[u8], hash: &BareHashDigest) -> bool {
|
||||
pub async fn validate_hash(&self, data: &[u8], hash: &HashDigest) -> VeilidAPIResult<bool> {
|
||||
yielding(|| self.guard.validate_hash(data, hash)).await
|
||||
}
|
||||
|
||||
pub async fn validate_hash_reader(
|
||||
&self,
|
||||
reader: &mut dyn std::io::Read,
|
||||
hash: &BareHashDigest,
|
||||
hash: &HashDigest,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
yielding(|| self.guard.validate_hash_reader(reader, hash)).await
|
||||
}
|
||||
|
||||
// Distance Metric
|
||||
pub async fn distance(&self, key1: &BareHashDigest, key2: &BareHashDigest) -> BareHashDistance {
|
||||
yielding(|| self.guard.distance(key1, key2)).await
|
||||
}
|
||||
|
||||
// Authentication
|
||||
pub async fn sign(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
key: &PublicKey,
|
||||
secret: &SecretKey,
|
||||
data: &[u8],
|
||||
) -> VeilidAPIResult<BareSignature> {
|
||||
) -> VeilidAPIResult<Signature> {
|
||||
yielding(|| self.guard.sign(key, secret, data)).await
|
||||
}
|
||||
pub async fn verify(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
key: &PublicKey,
|
||||
data: &[u8],
|
||||
signature: &BareSignature,
|
||||
signature: &Signature,
|
||||
) -> VeilidAPIResult<bool> {
|
||||
yielding(|| self.guard.verify(key, data, signature)).await
|
||||
}
|
||||
|
@ -215,8 +219,8 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
pub async fn decrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()> {
|
||||
yielding(|| {
|
||||
|
@ -229,8 +233,8 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
pub async fn decrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
yielding(|| {
|
||||
|
@ -243,8 +247,8 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
pub async fn encrypt_in_place_aead(
|
||||
&self,
|
||||
body: &mut Vec<u8>,
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<()> {
|
||||
yielding(|| {
|
||||
|
@ -257,8 +261,8 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
pub async fn encrypt_aead(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
yielding(|| {
|
||||
|
@ -272,8 +276,8 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
pub async fn crypt_in_place_no_auth(
|
||||
&self,
|
||||
body: &mut [u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<()> {
|
||||
yielding(|| {
|
||||
self.guard
|
||||
|
@ -286,8 +290,8 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
&self,
|
||||
in_buf: &[u8],
|
||||
out_buf: &mut [u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<()> {
|
||||
yielding(|| {
|
||||
self.guard
|
||||
|
@ -299,8 +303,8 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
pub async fn crypt_no_auth_aligned_8(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
yielding(|| {
|
||||
self.guard
|
||||
|
@ -312,8 +316,8 @@ impl AsyncCryptoSystemGuard<'_> {
|
|||
pub async fn crypt_no_auth_unaligned(
|
||||
&self,
|
||||
body: &[u8],
|
||||
nonce: &BareNonce,
|
||||
shared_secret: &BareSharedSecret,
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
yielding(|| {
|
||||
self.guard
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
mod blake3digest512;
|
||||
mod dh_cache;
|
||||
mod envelope;
|
||||
mod guard;
|
||||
|
@ -6,41 +5,20 @@ mod receipt;
|
|||
mod types;
|
||||
|
||||
pub mod crypto_system;
|
||||
#[cfg(feature = "enable-crypto-none")]
|
||||
pub(crate) mod none;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod tests;
|
||||
#[cfg(feature = "enable-crypto-vld0")]
|
||||
pub(crate) mod vld0;
|
||||
// #[cfg(feature = "enable-crypto-vld1")]
|
||||
// pub(crate) mod vld1;
|
||||
|
||||
pub use blake3digest512::*;
|
||||
|
||||
pub use crypto_system::*;
|
||||
use dh_cache::*;
|
||||
pub(crate) use envelope::*;
|
||||
pub use guard::*;
|
||||
pub(crate) use receipt::*;
|
||||
pub use types::*;
|
||||
|
||||
#[cfg(feature = "enable-crypto-none")]
|
||||
pub use none::sizes::*;
|
||||
#[cfg(feature = "enable-crypto-none")]
|
||||
pub(crate) use none::*;
|
||||
#[cfg(feature = "enable-crypto-vld0")]
|
||||
pub use vld0::sizes::*;
|
||||
#[cfg(feature = "enable-crypto-vld0")]
|
||||
pub(crate) use vld0::*;
|
||||
// #[cfg(feature = "enable-crypto-vld1")]
|
||||
// pub(crate) use vld1::*;
|
||||
|
||||
use super::*;
|
||||
use core::convert::TryInto;
|
||||
use dh_cache::*;
|
||||
use hashlink::linked_hash_map::Entry;
|
||||
use hashlink::LruCache;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
impl_veilid_log_facility!("crypto");
|
||||
|
||||
|
@ -67,8 +45,9 @@ cfg_if! {
|
|||
}
|
||||
/// Number of cryptosystem signatures to keep on structures if many are present beyond the ones we consider valid
|
||||
pub const MAX_CRYPTO_KINDS: usize = 3;
|
||||
|
||||
/// Return the best cryptosystem kind we support
|
||||
pub fn best_crypto_kind() -> CryptoKind {
|
||||
pub(crate) fn best_crypto_kind() -> CryptoKind {
|
||||
VALID_CRYPTO_KINDS[0]
|
||||
}
|
||||
|
||||
|
@ -160,16 +139,23 @@ impl Crypto {
|
|||
.open("crypto_caches", 1)
|
||||
.await
|
||||
.wrap_err("failed to open crypto_caches")?;
|
||||
let caches_valid = match db.load(0, b"cache_validity_key").await? {
|
||||
let mut caches_valid = match db.load(0, b"cache_validity_key").await? {
|
||||
Some(v) => v == cache_validity_key,
|
||||
None => false,
|
||||
};
|
||||
|
||||
if caches_valid {
|
||||
if let Some(b) = db.load(0, b"dh_cache").await? {
|
||||
let mut inner = self.inner.lock();
|
||||
bytes_to_cache(&b, &mut inner.dh_cache);
|
||||
if let Ok(dh_cache) = bytes_to_cache(&b) {
|
||||
inner.dh_cache = dh_cache;
|
||||
} else {
|
||||
caches_valid = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if !caches_valid {
|
||||
drop(db);
|
||||
table_store.delete("crypto_caches").await?;
|
||||
db = table_store.open("crypto_caches", 1).await?;
|
||||
|
@ -245,12 +231,12 @@ impl Crypto {
|
|||
}
|
||||
|
||||
// Factory method to get the best crypto version
|
||||
pub fn best(&self) -> CryptoSystemGuard<'_> {
|
||||
pub(crate) fn best(&self) -> CryptoSystemGuard<'_> {
|
||||
self.get(best_crypto_kind()).unwrap()
|
||||
}
|
||||
|
||||
// Factory method to get the best crypto version for async use
|
||||
pub fn best_async(&self) -> AsyncCryptoSystemGuard<'_> {
|
||||
pub(crate) fn best_async(&self) -> AsyncCryptoSystemGuard<'_> {
|
||||
self.get_async(best_crypto_kind()).unwrap()
|
||||
}
|
||||
|
||||
|
@ -261,17 +247,17 @@ impl Crypto {
|
|||
&self,
|
||||
public_keys: &[PublicKey],
|
||||
data: &[u8],
|
||||
typed_signatures: &[Signature],
|
||||
signatures: &[Signature],
|
||||
) -> VeilidAPIResult<Option<PublicKeyGroup>> {
|
||||
let mut out = PublicKeyGroup::with_capacity(public_keys.len());
|
||||
for sig in typed_signatures {
|
||||
for nid in public_keys {
|
||||
if nid.kind() == sig.kind() {
|
||||
if let Some(vcrypto) = self.get(sig.kind()) {
|
||||
if !vcrypto.verify(nid.ref_value(), data, sig.ref_value())? {
|
||||
for signature in signatures {
|
||||
for public_key in public_keys {
|
||||
if public_key.kind() == signature.kind() {
|
||||
if let Some(vcrypto) = self.get(signature.kind()) {
|
||||
if !vcrypto.verify(public_key, data, signature)? {
|
||||
return Ok(None);
|
||||
}
|
||||
out.add(nid.clone());
|
||||
out.add(public_key.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,17 +271,16 @@ impl Crypto {
|
|||
pub fn generate_signatures<F, R>(
|
||||
&self,
|
||||
data: &[u8],
|
||||
typed_key_pairs: &[KeyPair],
|
||||
key_pairs: &[KeyPair],
|
||||
transform: F,
|
||||
) -> VeilidAPIResult<Vec<R>>
|
||||
where
|
||||
F: Fn(&KeyPair, BareSignature) -> R,
|
||||
F: Fn(&KeyPair, Signature) -> R,
|
||||
{
|
||||
let mut out = Vec::<R>::with_capacity(typed_key_pairs.len());
|
||||
for kp in typed_key_pairs {
|
||||
let mut out = Vec::<R>::with_capacity(key_pairs.len());
|
||||
for kp in key_pairs {
|
||||
if let Some(vcrypto) = self.get(kp.kind()) {
|
||||
let sig =
|
||||
vcrypto.sign(kp.ref_value().ref_key(), kp.ref_value().ref_secret(), data)?;
|
||||
let sig = vcrypto.sign(&kp.key(), &kp.secret(), data)?;
|
||||
out.push(transform(kp, sig))
|
||||
}
|
||||
}
|
||||
|
@ -308,12 +293,12 @@ impl Crypto {
|
|||
#[cfg(feature = "enable-crypto-vld0")]
|
||||
if crypto_kind == CRYPTO_KIND_VLD0 {
|
||||
let kp = vld0_generate_keypair();
|
||||
return Ok(KeyPair::new(crypto_kind, kp));
|
||||
return Ok(kp);
|
||||
}
|
||||
#[cfg(feature = "enable-crypto-none")]
|
||||
if crypto_kind == CRYPTO_KIND_NONE {
|
||||
let kp = none_generate_keypair();
|
||||
return Ok(KeyPair::new(crypto_kind, kp));
|
||||
return Ok(kp);
|
||||
}
|
||||
Err(VeilidAPIError::generic("invalid crypto kind"))
|
||||
}
|
||||
|
@ -323,9 +308,11 @@ impl Crypto {
|
|||
fn cached_dh_internal<T: CryptoSystem>(
|
||||
&self,
|
||||
vcrypto: &T,
|
||||
key: &BarePublicKey,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<BareSharedSecret> {
|
||||
key: &PublicKey,
|
||||
secret: &SecretKey,
|
||||
) -> VeilidAPIResult<SharedSecret> {
|
||||
vcrypto.check_public_key(key)?;
|
||||
vcrypto.check_secret_key(secret)?;
|
||||
Ok(
|
||||
match self.inner.lock().dh_cache.entry(DHCacheKey {
|
||||
key: key.clone(),
|
||||
|
@ -430,10 +417,7 @@ impl Crypto {
|
|||
let (public_key, secret_key) =
|
||||
if let (Some(public_key), Some(secret_key)) = (public_key, secret_key) {
|
||||
// Validate node id
|
||||
if !vcrypto
|
||||
.validate_keypair(&public_key.value(), &secret_key.value())
|
||||
.await
|
||||
{
|
||||
if !vcrypto.validate_keypair(&public_key, &secret_key).await? {
|
||||
apibail_generic!(format!(
|
||||
"secret_key and public_key don't match:\npublic_key: {}\nsecret_key: {}",
|
||||
public_key, secret_key
|
||||
|
@ -443,11 +427,7 @@ impl Crypto {
|
|||
} else {
|
||||
// If we still don't have a valid keypair, generate one
|
||||
veilid_log!(self debug "generating new node {} keypair", ck);
|
||||
let kp = vcrypto.generate_keypair().await;
|
||||
(
|
||||
PublicKey::new(ck, kp.key()),
|
||||
SecretKey::new(ck, kp.secret()),
|
||||
)
|
||||
vcrypto.generate_keypair().await.into_split()
|
||||
};
|
||||
|
||||
veilid_log!(self info "Public Key: {}", public_key);
|
||||
|
@ -476,13 +456,7 @@ impl Crypto {
|
|||
.expect("Valid crypto kind is not actually valid.");
|
||||
|
||||
#[cfg(test)]
|
||||
let (public_key, secret_key) = {
|
||||
let kp = vcrypto.generate_keypair().await;
|
||||
(
|
||||
PublicKey::new(ck, kp.key()),
|
||||
SecretKey::new(ck, kp.secret()),
|
||||
)
|
||||
};
|
||||
let (public_key, secret_key) = vcrypto.generate_keypair().await.into_split();
|
||||
#[cfg(not(test))]
|
||||
let (public_key, secret_key) = self.setup_public_key(vcrypto, table_store).await?;
|
||||
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
#![allow(clippy::absurd_extreme_comparisons)]
|
||||
|
||||
use super::*;
|
||||
use crate::*;
|
||||
use core::convert::TryInto;
|
||||
|
||||
pub const MAX_RECEIPT_SIZE: usize = 1380;
|
||||
pub const MAX_EXTRA_DATA_SIZE: usize = MAX_RECEIPT_SIZE - MIN_RECEIPT_SIZE; // 1250
|
||||
pub const MIN_RECEIPT_SIZE: usize = 130;
|
||||
|
||||
// Version number of receipt format
|
||||
fourcc_type!(ReceiptVersion);
|
||||
|
||||
pub const RECEIPT_VERSION_VLD0: ReceiptVersion = ReceiptVersion(*b"RCP0");
|
||||
// pub const RECEIPT_VERSION_VLD0_FOURCC: u32 = u32::from_be_bytes(*b"RCP0");
|
||||
|
||||
/// Receipt versions in order of preference, best receipt version is the first one, worst is the last one
|
||||
pub const VALID_RECEIPT_VERSIONS: [ReceiptVersion; 1] = [RECEIPT_VERSION_VLD0];
|
||||
|
||||
static_assertions::const_assert_eq!(VALID_RECEIPT_VERSIONS.len(), VALID_ENVELOPE_VERSIONS.len());
|
||||
|
||||
/// Return the best receipt version we support
|
||||
pub fn best_receipt_version() -> ReceiptVersion {
|
||||
VALID_RECEIPT_VERSIONS[0]
|
||||
}
|
||||
|
||||
/// Out-of-band receipts are versioned along with envelopes.
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct ReceiptHeader {
|
||||
/// // Size is at least 4 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
|
||||
/// version: [u8; 4], // 0x00: 0x52 0x43 0x50 0x30 ("RCP0")
|
||||
/// }
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct ReceiptV0 {
|
||||
/// // Size is 66 bytes without extra data and signature, 130 with signature
|
||||
/// version: [u8; 4], // 0x00: 0x52 0x43 0x50 0x30 ("RCP0")
|
||||
/// crypto_kind: [u8; 4], // 0x04: CryptoSystemVersion FOURCC code
|
||||
/// size: u16, // 0x08: Total size of the receipt including the extra data and the signature. Maximum size is 1380 bytes.
|
||||
/// nonce: [u8; 24], // 0x0A: Randomly chosen bytes that represent a unique receipt. Could be used to encrypt the extra data, but it's not required.
|
||||
/// sender_id: [u8; 32], // 0x22: Node ID of the message source, which is the public key of the sender
|
||||
/// extra_data: [u8; ??], // 0x42: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1250 bytes)
|
||||
/// signature: [u8; 64], // 0x?? (end-0x40): BareSignature of the entire receipt including header and extra data is appended to the packet
|
||||
/// }
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Receipt {
|
||||
version: ReceiptVersion,
|
||||
crypto_kind: CryptoKind,
|
||||
nonce: BareNonce,
|
||||
sender_id: BareNodeId,
|
||||
extra_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Receipt {
|
||||
pub fn try_new<D: AsRef<[u8]>>(
|
||||
version: ReceiptVersion,
|
||||
crypto_kind: CryptoKind,
|
||||
nonce: BareNonce,
|
||||
sender_id: BareNodeId,
|
||||
extra_data: D,
|
||||
) -> VeilidAPIResult<Self> {
|
||||
assert!(VALID_RECEIPT_VERSIONS.contains(&version));
|
||||
assert!(VALID_CRYPTO_KINDS.contains(&crypto_kind));
|
||||
|
||||
if extra_data.as_ref().len() > MAX_EXTRA_DATA_SIZE {
|
||||
apibail_parse_error!(
|
||||
"extra data too large for receipt",
|
||||
extra_data.as_ref().len()
|
||||
);
|
||||
}
|
||||
Ok(Self {
|
||||
version,
|
||||
crypto_kind,
|
||||
nonce,
|
||||
sender_id,
|
||||
extra_data: Vec::from(extra_data.as_ref()),
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "receipt", skip_all, err)]
|
||||
pub fn from_signed_data(crypto: &Crypto, data: &[u8]) -> VeilidAPIResult<Receipt> {
|
||||
// Ensure we are at least the length of the envelope
|
||||
if data.len() < MIN_RECEIPT_SIZE {
|
||||
apibail_parse_error!("receipt too small", data.len());
|
||||
}
|
||||
|
||||
// Check version
|
||||
let version: ReceiptVersion = data[0x00..0x04]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
if !VALID_RECEIPT_VERSIONS.contains(&version) {
|
||||
apibail_parse_error!("unsupported receipt version", version);
|
||||
}
|
||||
|
||||
// Check crypto kind
|
||||
let crypto_kind = CryptoKind::try_from(&data[0x04..0x08])?;
|
||||
let Some(vcrypto) = crypto.get(crypto_kind) else {
|
||||
apibail_parse_error!("unsupported crypto kind", crypto_kind);
|
||||
};
|
||||
|
||||
// Get size and ensure it matches the size of the envelope and is less than the maximum message size
|
||||
let size: u16 = u16::from_le_bytes(
|
||||
data[0x08..0x0A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
if (size as usize) > MAX_RECEIPT_SIZE {
|
||||
apibail_parse_error!("receipt size is too large", size);
|
||||
}
|
||||
if (size as usize) != data.len() {
|
||||
apibail_parse_error!(
|
||||
"size doesn't match receipt size",
|
||||
format!("size={} data.len()={}", size, data.len())
|
||||
);
|
||||
}
|
||||
|
||||
// Get sender id
|
||||
let sender_id = BareNodeId::new(
|
||||
data[0x22..0x42]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
|
||||
// Get signature
|
||||
let signature = BareSignature::new(
|
||||
data[(data.len() - 64)..]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
|
||||
// Validate signature
|
||||
if !vcrypto
|
||||
.verify(
|
||||
&sender_id.clone().into(),
|
||||
&data[0..(data.len() - 64)],
|
||||
&signature,
|
||||
)
|
||||
.map_err(VeilidAPIError::generic)?
|
||||
{
|
||||
apibail_parse_error!("signature failure in receipt", signature);
|
||||
}
|
||||
|
||||
// Get nonce
|
||||
let nonce: BareNonce = BareNonce::new(
|
||||
data[0x0A..0x22]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
|
||||
// Get extra data and signature
|
||||
let extra_data: Vec<u8> = Vec::from(&data[0x42..(data.len() - 64)]);
|
||||
|
||||
// Return receipt
|
||||
Ok(Self {
|
||||
version,
|
||||
crypto_kind,
|
||||
nonce,
|
||||
sender_id,
|
||||
extra_data,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "receipt", skip_all, err)]
|
||||
pub fn to_signed_data(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
secret: &BareSecretKey,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
// 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 version
|
||||
data[0x00..0x04].copy_from_slice(&self.version.0);
|
||||
// Write crypto kind
|
||||
data[0x04..0x08].copy_from_slice(&self.crypto_kind.0);
|
||||
// Write size
|
||||
data[0x08..0x0A].copy_from_slice(&(receipt_size as u16).to_le_bytes());
|
||||
// Write nonce
|
||||
data[0x0A..0x22].copy_from_slice(&self.nonce);
|
||||
// Write sender node id
|
||||
data[0x22..0x42].copy_from_slice(&self.sender_id);
|
||||
// Write extra data
|
||||
if !self.extra_data.is_empty() {
|
||||
data[0x42..(receipt_size - 64)].copy_from_slice(self.extra_data.as_slice());
|
||||
}
|
||||
// Sign the receipt
|
||||
let signature = vcrypto
|
||||
.sign(
|
||||
&self.sender_id.clone().into(),
|
||||
secret,
|
||||
&data[0..(receipt_size - 64)],
|
||||
)
|
||||
.map_err(VeilidAPIError::generic)?;
|
||||
// Append the signature
|
||||
data[(receipt_size - 64)..].copy_from_slice(&signature);
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_version(&self) -> ReceiptVersion {
|
||||
self.version
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_crypto_kind(&self) -> CryptoKind {
|
||||
self.crypto_kind
|
||||
}
|
||||
|
||||
pub fn get_nonce(&self) -> BareNonce {
|
||||
self.nonce.clone()
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_bare_sender_id(&self) -> BareNodeId {
|
||||
self.sender_id.clone()
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_sender_id(&self) -> NodeId {
|
||||
NodeId::new(self.crypto_kind, self.sender_id.clone())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_extra_data(&self) -> &[u8] {
|
||||
&self.extra_data
|
||||
}
|
||||
}
|
337
veilid-core/src/crypto/receipt/mod.rs
Normal file
337
veilid-core/src/crypto/receipt/mod.rs
Normal file
|
@ -0,0 +1,337 @@
|
|||
#![allow(clippy::absurd_extreme_comparisons)]
|
||||
|
||||
use super::*;
|
||||
use crate::routing_table::*;
|
||||
use core::convert::TryInto;
|
||||
|
||||
// Version number of receipt format
|
||||
fourcc_type!(ReceiptVersion);
|
||||
|
||||
// RCP0
|
||||
pub const RECEIPT_VERSION_RCP0: ReceiptVersion = ReceiptVersion::new(*b"RCP0");
|
||||
pub const RCP0_NONCE_LENGTH: usize = 24;
|
||||
pub const RCP0_SIGNATURE_LENGTH: usize = 64;
|
||||
pub const RCP0_MAX_RECEIPT_SIZE: usize = 1380;
|
||||
pub const RCP0_MAX_EXTRA_DATA_SIZE: usize = RCP0_MAX_RECEIPT_SIZE - RCP0_MIN_RECEIPT_SIZE; // 1250
|
||||
pub const RCP0_MIN_RECEIPT_SIZE: usize = 130;
|
||||
|
||||
/// Receipt versions in order of preference, best receipt version is the first one, worst is the last one
|
||||
pub const VALID_RECEIPT_VERSIONS: [ReceiptVersion; 1] = [RECEIPT_VERSION_RCP0];
|
||||
|
||||
static_assertions::const_assert_eq!(VALID_RECEIPT_VERSIONS.len(), VALID_ENVELOPE_VERSIONS.len());
|
||||
|
||||
/// Return the best receipt version we support
|
||||
pub fn best_receipt_version() -> ReceiptVersion {
|
||||
VALID_RECEIPT_VERSIONS[0]
|
||||
}
|
||||
|
||||
/// Out-of-band receipts are versioned along with envelopes.
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct ReceiptHeader {
|
||||
/// // Size is at least 4 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
|
||||
/// version: [u8; 4], // 0x00: 0x52 0x43 0x50 0x30 ("RCP0")
|
||||
/// }
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct ReceiptRCP0 {
|
||||
/// // Size is 66 bytes without extra data and signature, 130 with signature
|
||||
/// version: [u8; 4], // 0x00: 0x52 0x43 0x50 0x30 ("RCP0")
|
||||
/// crypto_kind: [u8; 4], // 0x04: CryptoSystemVersion FOURCC code
|
||||
/// size: u16, // 0x08: Total size of the receipt including the extra data and the signature. Maximum size is 1380 bytes.
|
||||
/// nonce: [u8; 24], // 0x0A: Randomly chosen bytes that represent a unique receipt. Could be used to encrypt the extra data, but it's not required.
|
||||
/// sender_id: [u8; 32], // 0x22: Node ID of the message source, which is the public key of the sender
|
||||
/// extra_data: [u8; ??], // 0x42: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1250 bytes)
|
||||
/// signature: [u8; 64], // 0x?? (end-0x40): BareSignature of the entire receipt including header and extra data is appended to the packet
|
||||
/// }
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Receipt {
|
||||
RCP0 { rcp0: ReceiptRCP0 },
|
||||
}
|
||||
|
||||
impl Receipt {
|
||||
#[instrument(level = "trace", target = "envelope", skip_all)]
|
||||
pub fn try_new_rcp0<D: AsRef<[u8]>>(
|
||||
crypto: &Crypto,
|
||||
crypto_kind: CryptoKind,
|
||||
nonce: Nonce,
|
||||
sender_id: NodeId,
|
||||
extra_data: D,
|
||||
) -> VeilidAPIResult<Self> {
|
||||
Ok(Self::RCP0 {
|
||||
rcp0: ReceiptRCP0::try_new(crypto, crypto_kind, nonce, sender_id, extra_data)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "receipt", skip_all, err)]
|
||||
pub fn try_from_signed_data(crypto: &Crypto, data: &[u8]) -> VeilidAPIResult<Receipt> {
|
||||
// Ensure we are at least the length of the envelope
|
||||
if data.len() < 4 {
|
||||
apibail_parse_error!("receipt header too small", data.len());
|
||||
}
|
||||
|
||||
// Check version
|
||||
let version: ReceiptVersion = data[0x00..0x04]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
|
||||
match version {
|
||||
RECEIPT_VERSION_RCP0 => Ok(Self::RCP0 {
|
||||
rcp0: ReceiptRCP0::try_from_signed_data(crypto, data)?,
|
||||
}),
|
||||
_ => {
|
||||
apibail_parse_error!("unsupported receipt version", version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "envelope", skip_all)]
|
||||
pub fn to_signed_data(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
secret_key: &SecretKey,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
match self {
|
||||
Receipt::RCP0 { rcp0 } => rcp0.to_signed_data(crypto, secret_key),
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_version(&self) -> ReceiptVersion {
|
||||
match self {
|
||||
Receipt::RCP0 { rcp0: _ } => RECEIPT_VERSION_RCP0,
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_crypto_kind(&self) -> CryptoKind {
|
||||
match self {
|
||||
Receipt::RCP0 { rcp0 } => rcp0.get_crypto_kind(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_nonce(&self) -> Nonce {
|
||||
match self {
|
||||
Receipt::RCP0 { rcp0 } => rcp0.get_nonce(),
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn get_sender_id(&self) -> NodeId {
|
||||
match self {
|
||||
Receipt::RCP0 { rcp0 } => rcp0.get_sender_id(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_extra_data(&self) -> &[u8] {
|
||||
match self {
|
||||
Receipt::RCP0 { rcp0 } => rcp0.get_extra_data(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ReceiptRCP0 {
|
||||
crypto_kind: CryptoKind,
|
||||
nonce: Nonce,
|
||||
bare_sender_id: BareNodeId,
|
||||
extra_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ReceiptRCP0 {
|
||||
pub fn try_new<D: AsRef<[u8]>>(
|
||||
crypto: &Crypto,
|
||||
crypto_kind: CryptoKind,
|
||||
nonce: Nonce,
|
||||
sender_id: NodeId,
|
||||
extra_data: D,
|
||||
) -> VeilidAPIResult<Self> {
|
||||
let vcrypto = Self::validate_crypto_kind(crypto, crypto_kind)?;
|
||||
|
||||
vcrypto.check_nonce(&nonce)?;
|
||||
Self::check_node_id(crypto_kind, &sender_id)?;
|
||||
|
||||
if extra_data.as_ref().len() > RCP0_MAX_EXTRA_DATA_SIZE {
|
||||
apibail_parse_error!(
|
||||
"extra data too large for receipt",
|
||||
extra_data.as_ref().len()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
crypto_kind,
|
||||
nonce,
|
||||
bare_sender_id: sender_id.value(),
|
||||
extra_data: Vec::from(extra_data.as_ref()),
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "receipt", skip_all, err)]
|
||||
pub fn try_from_signed_data(crypto: &Crypto, data: &[u8]) -> VeilidAPIResult<Self> {
|
||||
// Ensure we are at least the length of the envelope
|
||||
if data.len() < RCP0_MIN_RECEIPT_SIZE {
|
||||
apibail_parse_error!("receipt too small", data.len());
|
||||
}
|
||||
|
||||
// Check crypto kind
|
||||
let crypto_kind = CryptoKind::try_from(&data[0x04..0x08])?;
|
||||
let vcrypto = Self::validate_crypto_kind(crypto, crypto_kind)?;
|
||||
|
||||
// Get size and ensure it matches the size of the envelope and is less than the maximum message size
|
||||
let size: u16 = u16::from_le_bytes(
|
||||
data[0x08..0x0A]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
if (size as usize) > RCP0_MAX_RECEIPT_SIZE {
|
||||
apibail_parse_error!("receipt size is too large", size);
|
||||
}
|
||||
if (size as usize) != data.len() {
|
||||
apibail_parse_error!(
|
||||
"size doesn't match receipt size",
|
||||
format!("size={} data.len()={}", size, data.len())
|
||||
);
|
||||
}
|
||||
|
||||
// Get sender id
|
||||
let bare_sender_id = BareNodeId::new(
|
||||
data[0x22..0x42]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
let sender_public_key = PublicKey::new(crypto_kind, BarePublicKey::new(&bare_sender_id));
|
||||
|
||||
// Get signature
|
||||
let bare_signature = BareSignature::new(
|
||||
data[(data.len() - 64)..]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
|
||||
let signature = Signature::new(crypto_kind, bare_signature);
|
||||
|
||||
// Validate signature
|
||||
if !vcrypto
|
||||
.verify(&sender_public_key, &data[0..(data.len() - 64)], &signature)
|
||||
.map_err(VeilidAPIError::generic)?
|
||||
{
|
||||
apibail_parse_error!("signature failure in receipt", signature);
|
||||
}
|
||||
|
||||
// Get nonce
|
||||
let nonce: Nonce = Nonce::new(
|
||||
data[0x0A..0x22]
|
||||
.try_into()
|
||||
.map_err(VeilidAPIError::internal)?,
|
||||
);
|
||||
|
||||
// Get extra data and signature
|
||||
let extra_data: Vec<u8> = Vec::from(&data[0x42..(data.len() - 64)]);
|
||||
|
||||
// Return receipt
|
||||
Ok(Self {
|
||||
crypto_kind,
|
||||
nonce,
|
||||
bare_sender_id,
|
||||
extra_data,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", target = "receipt", skip_all, err)]
|
||||
pub fn to_signed_data(
|
||||
&self,
|
||||
crypto: &Crypto,
|
||||
secret_key: &SecretKey,
|
||||
) -> VeilidAPIResult<Vec<u8>> {
|
||||
let vcrypto = crypto
|
||||
.get(self.crypto_kind)
|
||||
.expect("need to ensure only valid crypto kinds here");
|
||||
vcrypto.check_secret_key(secret_key)?;
|
||||
|
||||
// Ensure extra data isn't too long
|
||||
let receipt_size: usize = self.extra_data.len() + RCP0_MIN_RECEIPT_SIZE;
|
||||
if receipt_size > RCP0_MAX_RECEIPT_SIZE {
|
||||
apibail_parse_error!("receipt too large", receipt_size);
|
||||
}
|
||||
|
||||
let mut data: Vec<u8> = vec![0u8; receipt_size];
|
||||
|
||||
// Write version
|
||||
data[0x00..0x04].copy_from_slice(&RECEIPT_VERSION_RCP0.0);
|
||||
// Write crypto kind
|
||||
data[0x04..0x08].copy_from_slice(self.crypto_kind.bytes());
|
||||
// Write size
|
||||
data[0x08..0x0A].copy_from_slice(&(receipt_size as u16).to_le_bytes());
|
||||
// Write nonce
|
||||
data[0x0A..0x22].copy_from_slice(&self.nonce);
|
||||
// Write sender node id
|
||||
data[0x22..0x42].copy_from_slice(&self.bare_sender_id);
|
||||
// Write extra data
|
||||
if !self.extra_data.is_empty() {
|
||||
data[0x42..(receipt_size - RCP0_SIGNATURE_LENGTH)]
|
||||
.copy_from_slice(self.extra_data.as_slice());
|
||||
}
|
||||
// Sign the receipt
|
||||
let sender_public_key =
|
||||
PublicKey::new(self.crypto_kind, BarePublicKey::new(&self.bare_sender_id));
|
||||
let signature = vcrypto
|
||||
.sign(
|
||||
&sender_public_key,
|
||||
secret_key,
|
||||
&data[0..(receipt_size - RCP0_SIGNATURE_LENGTH)],
|
||||
)
|
||||
.map_err(VeilidAPIError::generic)?;
|
||||
// Append the signature
|
||||
data[(receipt_size - 64)..].copy_from_slice(signature.ref_value());
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn get_crypto_kind(&self) -> CryptoKind {
|
||||
self.crypto_kind
|
||||
}
|
||||
|
||||
pub fn get_nonce(&self) -> Nonce {
|
||||
self.nonce.clone()
|
||||
}
|
||||
|
||||
pub fn get_sender_id(&self) -> NodeId {
|
||||
NodeId::new(self.crypto_kind, self.bare_sender_id.clone())
|
||||
}
|
||||
|
||||
pub fn get_extra_data(&self) -> &[u8] {
|
||||
&self.extra_data
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
fn validate_crypto_kind(
|
||||
crypto: &Crypto,
|
||||
crypto_kind: CryptoKind,
|
||||
) -> VeilidAPIResult<CryptoSystemGuard<'_>> {
|
||||
let vcrypto = crypto
|
||||
.get(crypto_kind)
|
||||
.ok_or_else(|| VeilidAPIError::parse_error("unsupported crypto kind", crypto_kind))?;
|
||||
|
||||
// Verify crypto kind can be used with this envelope
|
||||
if vcrypto.nonce_length() != RCP0_NONCE_LENGTH
|
||||
|| vcrypto.hash_digest_length() != HASH_COORDINATE_LENGTH
|
||||
|| vcrypto.public_key_length() != HASH_COORDINATE_LENGTH
|
||||
{
|
||||
apibail_generic!("unsupported crypto kind for this envelope type");
|
||||
}
|
||||
|
||||
Ok(vcrypto)
|
||||
}
|
||||
|
||||
fn check_node_id(crypto_kind: CryptoKind, node_id: &NodeId) -> VeilidAPIResult<()> {
|
||||
if node_id.kind() != crypto_kind {
|
||||
apibail_parse_error!("invalid crypto kind for RCP0", node_id.kind());
|
||||
}
|
||||
if node_id.ref_value().len() != HASH_COORDINATE_LENGTH {
|
||||
apibail_parse_error!("invalid node_id length for RCP0", node_id.ref_value().len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -132,6 +132,13 @@ pub fn fix_fake_public_key() -> PublicKey {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn fix_fake_secret_key() -> SecretKey {
|
||||
SecretKey::new(
|
||||
CryptoKind::from_str("FAKE").unwrap(),
|
||||
fix_fake_bare_secret_key(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fix_fake_bare_secret_key() -> BareSecretKey {
|
||||
let mut fake_key = [0u8; VLD0_SECRET_KEY_LENGTH];
|
||||
random_bytes(&mut fake_key);
|
||||
|
|
|
@ -182,9 +182,15 @@ pub async fn test_no_auth(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
|||
pub async fn test_dh(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
||||
trace!("test_dh");
|
||||
let (dht_key, dht_key_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
assert!(vcrypto.validate_keypair(&dht_key, &dht_key_secret).await);
|
||||
assert!(vcrypto
|
||||
.validate_keypair(&dht_key, &dht_key_secret)
|
||||
.await
|
||||
.expect("should succeed"));
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split();
|
||||
assert!(vcrypto.validate_keypair(&dht_key2, &dht_key_secret2).await);
|
||||
assert!(vcrypto
|
||||
.validate_keypair(&dht_key2, &dht_key_secret2)
|
||||
.await
|
||||
.expect("should succeed"));
|
||||
|
||||
let r1 = vcrypto
|
||||
.compute_dh(&dht_key, &dht_key_secret2)
|
||||
|
|
|
@ -18,16 +18,25 @@ pub async fn test_envelope_round_trip(
|
|||
// Create envelope
|
||||
let ts = Timestamp::from(0x12345678ABCDEF69u64);
|
||||
let nonce = vcrypto.random_nonce().await;
|
||||
let (sender_id, sender_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
let (recipient_id, recipient_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
let envelope = Envelope::new(
|
||||
envelope_version,
|
||||
vcrypto.kind(),
|
||||
ts,
|
||||
nonce,
|
||||
sender_id.into(),
|
||||
recipient_id.into(),
|
||||
);
|
||||
let (sender_public_key, sender_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
let sender_id = crypto
|
||||
.routing_table()
|
||||
.generate_node_id(&sender_public_key)
|
||||
.expect("should generate node id");
|
||||
let (recipient_public_key, recipient_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
let recipient_id = crypto
|
||||
.routing_table()
|
||||
.generate_node_id(&recipient_public_key)
|
||||
.expect("should generate node id");
|
||||
let envelope = match envelope_version {
|
||||
ENVELOPE_VERSION_ENV0 => {
|
||||
Envelope::try_new_env0(&crypto, vcrypto.kind(), ts, nonce, sender_id, recipient_id)
|
||||
.expect("should create envelope")
|
||||
}
|
||||
_ => {
|
||||
panic!("unsupported envelope version");
|
||||
}
|
||||
};
|
||||
|
||||
// Create arbitrary body
|
||||
let body = b"This is an arbitrary body";
|
||||
|
@ -38,7 +47,7 @@ pub async fn test_envelope_round_trip(
|
|||
.expect("failed to encrypt data");
|
||||
|
||||
// Deserialize from bytes
|
||||
let envelope2 = Envelope::from_signed_data(&crypto, &enc_data, &network_key)
|
||||
let envelope2 = Envelope::try_from_signed_data(&crypto, &enc_data, &network_key)
|
||||
.expect("failed to deserialize envelope from data");
|
||||
|
||||
let body2 = envelope2
|
||||
|
@ -54,13 +63,13 @@ pub async fn test_envelope_round_trip(
|
|||
let mut mod_enc_data = enc_data.clone();
|
||||
mod_enc_data[enc_data_len - 1] ^= 0x80u8;
|
||||
assert!(
|
||||
Envelope::from_signed_data(&crypto, &mod_enc_data, &network_key).is_err(),
|
||||
Envelope::try_from_signed_data(&crypto, &mod_enc_data, &network_key).is_err(),
|
||||
"should have failed to decode envelope with modified signature"
|
||||
);
|
||||
let mut mod_enc_data2 = enc_data.clone();
|
||||
mod_enc_data2[enc_data_len - 65] ^= 0x80u8;
|
||||
assert!(
|
||||
Envelope::from_signed_data(&crypto, &mod_enc_data2, &network_key).is_err(),
|
||||
Envelope::try_from_signed_data(&crypto, &mod_enc_data2, &network_key).is_err(),
|
||||
"should have failed to decode envelope with modified data"
|
||||
);
|
||||
}
|
||||
|
@ -76,15 +85,20 @@ pub async fn test_receipt_round_trip(
|
|||
|
||||
// Create receipt
|
||||
let nonce = vcrypto.random_nonce().await;
|
||||
let (sender_id, sender_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
let receipt = Receipt::try_new(
|
||||
receipt_version,
|
||||
vcrypto.kind(),
|
||||
nonce,
|
||||
sender_id.into(),
|
||||
body,
|
||||
)
|
||||
.expect("should not fail");
|
||||
let (sender_public_key, sender_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
let sender_id = crypto
|
||||
.routing_table()
|
||||
.generate_node_id(&sender_public_key)
|
||||
.expect("should generate node id");
|
||||
let receipt = match receipt_version {
|
||||
RECEIPT_VERSION_RCP0 => {
|
||||
Receipt::try_new_rcp0(&crypto, vcrypto.kind(), nonce, sender_id, body)
|
||||
.expect("should not fail")
|
||||
}
|
||||
_ => {
|
||||
panic!("unsupported receipt version");
|
||||
}
|
||||
};
|
||||
|
||||
// Serialize to bytes
|
||||
let mut enc_data = receipt
|
||||
|
@ -92,12 +106,12 @@ pub async fn test_receipt_round_trip(
|
|||
.expect("failed to make signed data");
|
||||
|
||||
// Deserialize from bytes
|
||||
let receipt2 = Receipt::from_signed_data(&crypto, &enc_data)
|
||||
let receipt2 = Receipt::try_from_signed_data(&crypto, &enc_data)
|
||||
.expect("failed to deserialize envelope from data");
|
||||
|
||||
// Should not validate even when a single bit is changed
|
||||
enc_data[5] = 0x01;
|
||||
let _ = Receipt::from_signed_data(&crypto, &enc_data)
|
||||
let _ = Receipt::try_from_signed_data(&crypto, &enc_data)
|
||||
.expect_err("should have failed to decrypt using wrong secret");
|
||||
|
||||
// Compare receipts
|
||||
|
@ -114,8 +128,12 @@ pub async fn test_all() {
|
|||
let vcrypto = crypto.get_async(v).unwrap();
|
||||
|
||||
test_envelope_round_trip(ev, &vcrypto, None).await;
|
||||
test_envelope_round_trip(ev, &vcrypto, Some(vcrypto.random_shared_secret().await))
|
||||
.await;
|
||||
test_envelope_round_trip(
|
||||
ev,
|
||||
&vcrypto,
|
||||
Some(vcrypto.random_shared_secret().await.into_value()),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,162 +3,188 @@ use crate::crypto::tests::fixtures::*;
|
|||
|
||||
pub async fn test_generate_secret(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
||||
// Verify keys generate
|
||||
let (dht_key, dht_key_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split();
|
||||
let (public_key, secret_key) = vcrypto.generate_keypair().await.into_split();
|
||||
let (public_key2, secret_key2) = vcrypto.generate_keypair().await.into_split();
|
||||
|
||||
// Verify byte patterns are different between public and secret
|
||||
assert_ne!(dht_key.bytes(), dht_key_secret.bytes());
|
||||
assert_ne!(dht_key2.bytes(), dht_key_secret2.bytes());
|
||||
assert_ne!(
|
||||
public_key.ref_value().bytes(),
|
||||
secret_key.ref_value().bytes()
|
||||
);
|
||||
assert_ne!(
|
||||
public_key2.ref_value().bytes(),
|
||||
secret_key2.ref_value().bytes()
|
||||
);
|
||||
|
||||
// Verify the keys and secrets are different across keypairs
|
||||
assert_ne!(dht_key, dht_key2);
|
||||
assert_ne!(dht_key_secret, dht_key_secret2);
|
||||
assert_ne!(public_key, public_key2);
|
||||
assert_ne!(secret_key, secret_key2);
|
||||
}
|
||||
|
||||
pub async fn test_sign_and_verify(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
||||
// Make two keys
|
||||
let (dht_key, dht_key_secret) = vcrypto.generate_keypair().await.into_split();
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split();
|
||||
let (public_key, secret_key) = vcrypto.generate_keypair().await.into_split();
|
||||
let (public_key2, secret_key2) = vcrypto.generate_keypair().await.into_split();
|
||||
// Sign the same message twice
|
||||
let dht_sig = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM)
|
||||
let sig = vcrypto
|
||||
.sign(&public_key, &secret_key, LOREM_IPSUM)
|
||||
.await
|
||||
.unwrap();
|
||||
trace!("dht_sig: {:?}", dht_sig);
|
||||
let dht_sig_b = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM)
|
||||
trace!("sig: {:?}", sig);
|
||||
let sig_b = vcrypto
|
||||
.sign(&public_key, &secret_key, LOREM_IPSUM)
|
||||
.await
|
||||
.unwrap();
|
||||
// Sign a second message
|
||||
let dht_sig_c = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret, CHEEZBURGER)
|
||||
let sig_c = vcrypto
|
||||
.sign(&public_key, &secret_key, CHEEZBURGER)
|
||||
.await
|
||||
.unwrap();
|
||||
trace!("dht_sig_c: {:?}", dht_sig_c);
|
||||
trace!("sig_c: {:?}", sig_c);
|
||||
// Verify they are the same signature
|
||||
assert_eq!(dht_sig, dht_sig_b);
|
||||
assert_eq!(sig, sig_b);
|
||||
// Sign the same message with a different key
|
||||
let dht_sig2 = vcrypto
|
||||
.sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM)
|
||||
let sig2 = vcrypto
|
||||
.sign(&public_key2, &secret_key2, LOREM_IPSUM)
|
||||
.await
|
||||
.unwrap();
|
||||
// Verify a different key gives a different signature
|
||||
assert_ne!(dht_sig2, dht_sig_b);
|
||||
assert_ne!(sig2, sig_b);
|
||||
|
||||
// Try using the wrong secret to sign
|
||||
let a1 = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM)
|
||||
.sign(&public_key, &secret_key, LOREM_IPSUM)
|
||||
.await
|
||||
.unwrap();
|
||||
let a2 = vcrypto
|
||||
.sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM)
|
||||
.sign(&public_key2, &secret_key2, LOREM_IPSUM)
|
||||
.await
|
||||
.unwrap();
|
||||
let _b1 = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret2, LOREM_IPSUM)
|
||||
.sign(&public_key, &secret_key2, LOREM_IPSUM)
|
||||
.await
|
||||
.unwrap_err();
|
||||
let _b2 = vcrypto
|
||||
.sign(&dht_key2, &dht_key_secret, LOREM_IPSUM)
|
||||
.sign(&public_key2, &secret_key, LOREM_IPSUM)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert_ne!(a1, a2);
|
||||
|
||||
assert_eq!(vcrypto.verify(&dht_key, LOREM_IPSUM, &a1).await, Ok(true));
|
||||
assert_eq!(vcrypto.verify(&dht_key2, LOREM_IPSUM, &a2).await, Ok(true));
|
||||
assert_eq!(vcrypto.verify(&dht_key, LOREM_IPSUM, &a2).await, Ok(false));
|
||||
assert_eq!(vcrypto.verify(&dht_key2, LOREM_IPSUM, &a1).await, Ok(false));
|
||||
assert_eq!(
|
||||
vcrypto.verify(&public_key, LOREM_IPSUM, &a1).await,
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&public_key2, LOREM_IPSUM, &a2).await,
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&public_key, LOREM_IPSUM, &a2).await,
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&public_key2, LOREM_IPSUM, &a1).await,
|
||||
Ok(false)
|
||||
);
|
||||
|
||||
// Try verifications that should work
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM, &dht_sig).await,
|
||||
vcrypto.verify(&public_key, LOREM_IPSUM, &sig).await,
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM, &dht_sig_b).await,
|
||||
vcrypto.verify(&public_key, LOREM_IPSUM, &sig_b).await,
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, LOREM_IPSUM, &dht_sig2).await,
|
||||
vcrypto.verify(&public_key2, LOREM_IPSUM, &sig2).await,
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, CHEEZBURGER, &dht_sig_c).await,
|
||||
vcrypto.verify(&public_key, CHEEZBURGER, &sig_c).await,
|
||||
Ok(true)
|
||||
);
|
||||
// Try verifications that shouldn't work
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, LOREM_IPSUM, &dht_sig).await,
|
||||
vcrypto.verify(&public_key2, LOREM_IPSUM, &sig).await,
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM, &dht_sig2).await,
|
||||
vcrypto.verify(&public_key, LOREM_IPSUM, &sig2).await,
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, CHEEZBURGER, &dht_sig_c).await,
|
||||
vcrypto.verify(&public_key2, CHEEZBURGER, &sig_c).await,
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, CHEEZBURGER, &dht_sig).await,
|
||||
vcrypto.verify(&public_key, CHEEZBURGER, &sig).await,
|
||||
Ok(false)
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn test_key_conversions(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
||||
// Test default key
|
||||
let (dht_key, dht_key_secret) = (BarePublicKey::default(), BareSecretKey::default());
|
||||
assert!(dht_key.bytes().is_empty());
|
||||
assert!(dht_key_secret.bytes().is_empty());
|
||||
let dht_key_string = String::from(&dht_key);
|
||||
trace!("dht_key_string: {:?}", dht_key_string);
|
||||
let dht_key_string2 = String::from(&dht_key);
|
||||
trace!("dht_key_string2: {:?}", dht_key_string2);
|
||||
assert_eq!(dht_key_string, dht_key_string2);
|
||||
let (public_key, secret_key) = (
|
||||
PublicKey::new(
|
||||
vcrypto.kind(),
|
||||
BarePublicKey::new(&vec![0u8; vcrypto.public_key_length()]),
|
||||
),
|
||||
SecretKey::new(
|
||||
vcrypto.kind(),
|
||||
BareSecretKey::new(&vec![0u8; vcrypto.secret_key_length()]),
|
||||
),
|
||||
);
|
||||
let public_key_string = public_key.to_string();
|
||||
trace!("public_key_string: {:?}", public_key_string);
|
||||
let public_key_string2 = public_key.to_string();
|
||||
trace!("public_key_string2: {:?}", public_key_string2);
|
||||
assert_eq!(public_key_string, public_key_string2);
|
||||
|
||||
let dht_key_secret_string = String::from(&dht_key_secret);
|
||||
trace!("dht_key_secret_string: {:?}", dht_key_secret_string);
|
||||
assert_eq!(dht_key_secret_string, dht_key_string);
|
||||
let secret_key_string = secret_key.to_string();
|
||||
trace!("secret_key_string: {:?}", secret_key_string);
|
||||
assert_eq!(secret_key_string, public_key_string);
|
||||
|
||||
// Make different keys
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split();
|
||||
trace!("dht_key2: {:?}", dht_key2);
|
||||
trace!("dht_key_secret2: {:?}", dht_key_secret2);
|
||||
let (dht_key3, _dht_key_secret3) = vcrypto.generate_keypair().await.into_split();
|
||||
trace!("dht_key3: {:?}", dht_key3);
|
||||
trace!("_dht_key_secret3: {:?}", _dht_key_secret3);
|
||||
let (public_key2, secret_key2) = vcrypto.generate_keypair().await.into_split();
|
||||
trace!("public_key2: {:?}", public_key2);
|
||||
trace!("secret_key2: {:?}", secret_key2);
|
||||
let (public_key3, secret_key3) = vcrypto.generate_keypair().await.into_split();
|
||||
trace!("public_key3: {:?}", public_key3);
|
||||
trace!("secret_key3: {:?}", secret_key3);
|
||||
|
||||
let dht_key2_string = String::from(&dht_key2);
|
||||
let dht_key2_string2 = String::from(&dht_key2);
|
||||
let dht_key3_string = String::from(&dht_key3);
|
||||
assert_eq!(dht_key2_string, dht_key2_string2);
|
||||
assert_ne!(dht_key3_string, dht_key2_string);
|
||||
let dht_key_secret2_string = String::from(&dht_key_secret2);
|
||||
assert_ne!(dht_key_secret2_string, dht_key_secret_string);
|
||||
assert_ne!(dht_key_secret2_string, dht_key2_string);
|
||||
let public_key2_string = public_key2.to_string();
|
||||
let public_key2_string2 = public_key2.to_string();
|
||||
let public_key3_string = public_key3.to_string();
|
||||
assert_eq!(public_key2_string, public_key2_string2);
|
||||
assert_ne!(public_key3_string, public_key2_string);
|
||||
let secret_key2_string = secret_key2.to_string();
|
||||
assert_ne!(secret_key2_string, secret_key_string);
|
||||
assert_ne!(secret_key2_string, public_key2_string);
|
||||
|
||||
// Assert they convert back correctly
|
||||
let dht_key_back = BarePublicKey::try_from(dht_key_string.as_str()).unwrap();
|
||||
let dht_key_back2 = BarePublicKey::try_from(dht_key_string2.as_str()).unwrap();
|
||||
assert_eq!(dht_key_back, dht_key_back2);
|
||||
assert_eq!(dht_key_back, dht_key);
|
||||
assert_eq!(dht_key_back2, dht_key);
|
||||
let public_key_back = PublicKey::try_from(public_key_string.as_str()).unwrap();
|
||||
let public_key_back2 = PublicKey::try_from(public_key_string2.as_str()).unwrap();
|
||||
assert_eq!(public_key_back, public_key_back2);
|
||||
assert_eq!(public_key_back, public_key);
|
||||
assert_eq!(public_key_back2, public_key);
|
||||
|
||||
let dht_key_secret_back = BareSecretKey::try_from(dht_key_secret_string.as_str()).unwrap();
|
||||
assert_eq!(dht_key_secret_back, dht_key_secret);
|
||||
let secret_key_back = SecretKey::try_from(secret_key_string.as_str()).unwrap();
|
||||
assert_eq!(secret_key_back, secret_key);
|
||||
|
||||
let dht_key2_back = BarePublicKey::try_from(dht_key2_string.as_str()).unwrap();
|
||||
let dht_key2_back2 = BarePublicKey::try_from(dht_key2_string2.as_str()).unwrap();
|
||||
assert_eq!(dht_key2_back, dht_key2_back2);
|
||||
assert_eq!(dht_key2_back, dht_key2);
|
||||
assert_eq!(dht_key2_back2, dht_key2);
|
||||
let public_key2_back = PublicKey::try_from(public_key2_string.as_str()).unwrap();
|
||||
let public_key2_back2 = PublicKey::try_from(public_key2_string2.as_str()).unwrap();
|
||||
assert_eq!(public_key2_back, public_key2_back2);
|
||||
assert_eq!(public_key2_back, public_key2);
|
||||
assert_eq!(public_key2_back2, public_key2);
|
||||
|
||||
let dht_key_secret2_back = BareSecretKey::try_from(dht_key_secret2_string.as_str()).unwrap();
|
||||
assert_eq!(dht_key_secret2_back, dht_key_secret2);
|
||||
let secret_key2_back = SecretKey::try_from(secret_key2_string.as_str()).unwrap();
|
||||
assert_eq!(secret_key2_back, secret_key2);
|
||||
|
||||
// Assert string roundtrip
|
||||
assert_eq!(String::from(&dht_key2_back), dht_key2_string);
|
||||
assert_eq!(secret_key2_back.to_string(), secret_key2_string);
|
||||
|
||||
// These conversions should fail
|
||||
assert!(BarePublicKey::try_from("whatever!").is_err());
|
||||
assert!(BareSecretKey::try_from("whatever!").is_err());
|
||||
|
@ -167,41 +193,42 @@ pub async fn test_key_conversions(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
|||
}
|
||||
|
||||
pub async fn test_encode_decode(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
||||
let dht_key = BarePublicKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
|
||||
let dht_key_secret =
|
||||
let public_key =
|
||||
BarePublicKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
|
||||
let secret_key =
|
||||
BareSecretKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
|
||||
let dht_key_b = BarePublicKey::new(&EMPTY_KEY);
|
||||
let dht_key_secret_b = BareSecretKey::new(&EMPTY_KEY_SECRET);
|
||||
assert_eq!(dht_key, dht_key_b);
|
||||
assert_eq!(dht_key_secret, dht_key_secret_b);
|
||||
let public_key_b = BarePublicKey::new(&EMPTY_KEY);
|
||||
let secret_key_b = BareSecretKey::new(&EMPTY_KEY_SECRET);
|
||||
assert_eq!(public_key, public_key_b);
|
||||
assert_eq!(secret_key, secret_key_b);
|
||||
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().await.into_split();
|
||||
let (public_key2, secret_key2) = vcrypto.generate_keypair().await.value().into_split();
|
||||
|
||||
let e1 = dht_key.encode();
|
||||
let e1 = public_key.encode();
|
||||
trace!("e1: {:?}", e1);
|
||||
assert_eq!(e1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_owned());
|
||||
let e1s = dht_key_secret.encode();
|
||||
let e1s = secret_key.encode();
|
||||
trace!("e1s: {:?}", e1s);
|
||||
let e2 = dht_key2.encode();
|
||||
let e2 = public_key2.encode();
|
||||
trace!("e2: {:?}", e2);
|
||||
let e2s = dht_key_secret2.encode();
|
||||
let e2s = secret_key2.encode();
|
||||
trace!("e2s: {:?}", e2s);
|
||||
|
||||
let d1 = BarePublicKey::try_decode(e1.as_str()).unwrap();
|
||||
trace!("d1: {:?}", d1);
|
||||
assert_eq!(dht_key, d1);
|
||||
assert_eq!(public_key, d1);
|
||||
|
||||
let d1s = BareSecretKey::try_decode(e1s.as_str()).unwrap();
|
||||
trace!("d1s: {:?}", d1s);
|
||||
assert_eq!(dht_key_secret, d1s);
|
||||
assert_eq!(secret_key, d1s);
|
||||
|
||||
let d2 = BarePublicKey::try_decode(e2.as_str()).unwrap();
|
||||
trace!("d2: {:?}", d2);
|
||||
assert_eq!(dht_key2, d2);
|
||||
assert_eq!(public_key2, d2);
|
||||
|
||||
let d2s = BareSecretKey::try_decode(e2s.as_str()).unwrap();
|
||||
trace!("d2s: {:?}", d2s);
|
||||
assert_eq!(dht_key_secret2, d2s);
|
||||
assert_eq!(secret_key2, d2s);
|
||||
|
||||
// Failures
|
||||
let f1 = BareSecretKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!");
|
||||
|
@ -216,7 +243,7 @@ pub fn test_typed_convert(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
|||
vcrypto.kind()
|
||||
);
|
||||
let tk1 = PublicKey::from_str(&tks1).expect("failed");
|
||||
assert!(vcrypto.check_public_key(tk1.ref_value()).is_ok());
|
||||
assert!(vcrypto.check_public_key(&tk1).is_ok());
|
||||
let tks1x = tk1.to_string();
|
||||
assert_eq!(tks1, tks1x);
|
||||
|
||||
|
@ -231,7 +258,7 @@ pub fn test_typed_convert(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
|||
vcrypto.kind()
|
||||
);
|
||||
let tk3 = PublicKey::from_str(&tks3).expect("failed");
|
||||
assert!(vcrypto.check_public_key(tk3.ref_value()).is_err());
|
||||
assert!(vcrypto.check_public_key(&tk3).is_err());
|
||||
|
||||
let tks4 = "XXXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ".to_string();
|
||||
let tk4 = PublicKey::from_str(&tks4).expect("failed");
|
||||
|
@ -258,7 +285,7 @@ pub fn test_typed_convert(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
|||
}
|
||||
|
||||
async fn test_hash(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
||||
let mut s = BTreeSet::<BareHashDigest>::new();
|
||||
let mut s = BTreeSet::<HashDigest>::new();
|
||||
|
||||
let k1 = vcrypto.generate_hash("abc".as_bytes()).await;
|
||||
let k2 = vcrypto.generate_hash("abcd".as_bytes()).await;
|
||||
|
@ -289,24 +316,43 @@ async fn test_hash(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
|||
assert_eq!(k5, v5);
|
||||
assert_eq!(k6, v6);
|
||||
|
||||
vcrypto.validate_hash("abc".as_bytes(), &v1).await;
|
||||
vcrypto.validate_hash("abcd".as_bytes(), &v2).await;
|
||||
vcrypto.validate_hash("".as_bytes(), &v3).await;
|
||||
vcrypto.validate_hash(" ".as_bytes(), &v4).await;
|
||||
vcrypto.validate_hash(LOREM_IPSUM, &v5).await;
|
||||
vcrypto.validate_hash(CHEEZBURGER, &v6).await;
|
||||
vcrypto
|
||||
.validate_hash("abc".as_bytes(), &v1)
|
||||
.await
|
||||
.expect("should succeed");
|
||||
vcrypto
|
||||
.validate_hash("abcd".as_bytes(), &v2)
|
||||
.await
|
||||
.expect("should succeed");
|
||||
vcrypto
|
||||
.validate_hash("".as_bytes(), &v3)
|
||||
.await
|
||||
.expect("should succeed");
|
||||
vcrypto
|
||||
.validate_hash(" ".as_bytes(), &v4)
|
||||
.await
|
||||
.expect("should succeed");
|
||||
vcrypto
|
||||
.validate_hash(LOREM_IPSUM, &v5)
|
||||
.await
|
||||
.expect("should succeed");
|
||||
vcrypto
|
||||
.validate_hash(CHEEZBURGER, &v6)
|
||||
.await
|
||||
.expect("should succeed");
|
||||
}
|
||||
|
||||
async fn test_operations(vcrypto: &AsyncCryptoSystemGuard<'_>) {
|
||||
// xxx we should make this fixed byte arrays when we add another cryptosystem
|
||||
let k1 = vcrypto.generate_hash(LOREM_IPSUM).await;
|
||||
let k2 = vcrypto.generate_hash(CHEEZBURGER).await;
|
||||
let k3 = vcrypto.generate_hash("abc".as_bytes()).await;
|
||||
|
||||
// Get distance
|
||||
let d1 = vcrypto.distance(&k1, &k2).await;
|
||||
let d2 = vcrypto.distance(&k2, &k1).await;
|
||||
let d3 = vcrypto.distance(&k1, &k3).await;
|
||||
let d4 = vcrypto.distance(&k2, &k3).await;
|
||||
let d1 = k1.to_hash_coordinate().distance(&k2.to_hash_coordinate());
|
||||
let d2 = k2.to_hash_coordinate().distance(&k1.to_hash_coordinate());
|
||||
let d3 = k1.to_hash_coordinate().distance(&k3.to_hash_coordinate());
|
||||
let d4 = k2.to_hash_coordinate().distance(&k3.to_hash_coordinate());
|
||||
|
||||
trace!("d1={:?}", d1);
|
||||
trace!("d2={:?}", d2);
|
||||
|
|
|
@ -10,52 +10,20 @@ use data_encoding::BASE64URL_NOPAD;
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub trait Encodable
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn encode(&self) -> String;
|
||||
fn encoded_len(&self) -> usize;
|
||||
fn try_decode<S: AsRef<str>>(input: S) -> VeilidAPIResult<Self> {
|
||||
let b = input.as_ref().as_bytes();
|
||||
Self::try_decode_bytes(b)
|
||||
}
|
||||
fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult<Self>;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
pub trait ByteArrayType
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool;
|
||||
fn bytes(&self) -> &[u8];
|
||||
fn bit(&self, index: usize) -> bool;
|
||||
fn first_nonzero_bit(&self) -> Option<usize>;
|
||||
fn nibble(&self, index: usize) -> u8;
|
||||
fn first_nonzero_nibble(&self) -> Option<(usize, u8)>;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
macro_rules! byte_array_type {
|
||||
($name:ident) => {
|
||||
#[derive(Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(Tsify))]
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
tsify(from_wasm_abi, into_wasm_abi)
|
||||
)]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), serde(transparent))]
|
||||
($visibility:vis $name:ident) => {
|
||||
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(wasm_bindgen_derive::TryFromJsValue))]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
#[derive(Clone, Hash, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[must_use]
|
||||
pub struct $name {
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
tsify(type = "string")
|
||||
)]
|
||||
$visibility struct $name {
|
||||
bytes: Bytes,
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
make_wasm_bindgen_stubs!($name);
|
||||
|
||||
impl $name {
|
||||
pub fn new(data: &[u8]) -> Self {
|
||||
Self {
|
||||
|
@ -65,32 +33,69 @@ macro_rules! byte_array_type {
|
|||
fn new_from_bytes(bytes: Bytes) -> Self {
|
||||
Self { bytes }
|
||||
}
|
||||
}
|
||||
impl Default for $name {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bytes: Bytes::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ByteArrayType for $name {
|
||||
fn len(&self) -> usize {
|
||||
self.bytes.len()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.bytes.is_empty()
|
||||
}
|
||||
fn bytes(&self) -> &[u8] {
|
||||
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn first_nonzero_nibble(&self) -> Option<(usize, u8)> {
|
||||
for i in 0..(self.bytes.len() * 2) {
|
||||
let n = self.nibble(i);
|
||||
if n != 0 {
|
||||
return Some((i, n));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
#[wasm_bindgen]
|
||||
impl $name {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn js_new(data: &[u8]) -> Self {
|
||||
Self::new(data)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = parse)]
|
||||
pub fn js_parse(s: String) -> VeilidAPIResult<Self> {
|
||||
Self::from_str(&s)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toString)]
|
||||
pub fn js_to_string(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = isEqual)]
|
||||
pub fn js_is_equal(&self, other: &Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
|
||||
// TODO: add more typescript-only operations here
|
||||
}
|
||||
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
#[allow(dead_code)]
|
||||
impl $name {
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter, js_name = length))]
|
||||
pub fn len(&self) -> usize {
|
||||
self.bytes.len()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.bytes.is_empty()
|
||||
}
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
self.bytes.to_vec()
|
||||
}
|
||||
// Big endian bit ordering
|
||||
fn bit(&self, index: usize) -> bool {
|
||||
pub fn bit(&self, index: usize) -> bool {
|
||||
let bi = index / 8;
|
||||
let ti = 7 - (index % 8);
|
||||
((self.bytes[bi] >> ti) & 1) != 0
|
||||
}
|
||||
|
||||
fn first_nonzero_bit(&self) -> Option<usize> {
|
||||
pub fn first_nonzero_bit(&self) -> Option<usize> {
|
||||
for i in 0..self.bytes.len() {
|
||||
let b = self.bytes[i];
|
||||
if b != 0 {
|
||||
|
@ -106,7 +111,7 @@ macro_rules! byte_array_type {
|
|||
}
|
||||
|
||||
// Big endian nibble ordering
|
||||
fn nibble(&self, index: usize) -> u8 {
|
||||
pub fn nibble(&self, index: usize) -> u8 {
|
||||
let bi = index / 2;
|
||||
if index & 1 == 0 {
|
||||
(self.bytes[bi] >> 4) & 0xFu8
|
||||
|
@ -115,24 +120,17 @@ macro_rules! byte_array_type {
|
|||
}
|
||||
}
|
||||
|
||||
fn first_nonzero_nibble(&self) -> Option<(usize, u8)> {
|
||||
for i in 0..(self.bytes.len() * 2) {
|
||||
let n = self.nibble(i);
|
||||
if n != 0 {
|
||||
return Some((i, n));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
impl Encodable for $name {
|
||||
fn encode(&self) -> String {
|
||||
pub fn encode(&self) -> String {
|
||||
BASE64URL_NOPAD.encode(&self.bytes)
|
||||
}
|
||||
fn encoded_len(&self) -> usize {
|
||||
pub fn encoded_len(&self) -> usize {
|
||||
BASE64URL_NOPAD.encode_len(self.bytes.len())
|
||||
}
|
||||
fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult<Self> {
|
||||
pub fn try_decode(input: &str) -> VeilidAPIResult<Self> {
|
||||
let b = input.as_bytes();
|
||||
Self::try_decode_bytes(b)
|
||||
}
|
||||
pub fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult<Self> {
|
||||
if b.len() == 0 {
|
||||
return Ok(Self::default());
|
||||
}
|
||||
|
@ -218,9 +216,10 @@ macro_rules! byte_array_type {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<$name> for Vec<u8> {
|
||||
fn from(value: $name) -> Self {
|
||||
value.bytes().to_vec()
|
||||
impl TryFrom<$name> for Vec<u8> {
|
||||
type Error = VeilidAPIError;
|
||||
fn try_from(value: $name) -> Result<Self, Self::Error> {
|
||||
Ok(value.bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,75 +242,36 @@ macro_rules! byte_array_type {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
|
||||
byte_array_type!(BarePublicKey);
|
||||
byte_array_type!(BareSecretKey);
|
||||
byte_array_type!(BareEncapsulationKey);
|
||||
byte_array_type!(BareDecapsulationKey);
|
||||
byte_array_type!(BareSignature);
|
||||
byte_array_type!(BareNonce);
|
||||
// Untyped public key (variable length)
|
||||
byte_array_type!(pub BarePublicKey);
|
||||
// Untyped secret key (variable length)
|
||||
byte_array_type!(pub BareSecretKey);
|
||||
// Untyped encapsulation key (variable length)
|
||||
byte_array_type!(pub BareEncapsulationKey);
|
||||
// Untyped decapsulation key (variable length)
|
||||
byte_array_type!(pub BareDecapsulationKey);
|
||||
// Untyped signature (variable length)
|
||||
byte_array_type!(pub BareSignature);
|
||||
// Untyped hash digest (hashed to 32 bytes)
|
||||
byte_array_type!(pub BareHashDigest);
|
||||
// Untyped shared secret (variable length)
|
||||
byte_array_type!(pub BareSharedSecret);
|
||||
// Untyped record key (hashed to 32 bytes)
|
||||
byte_array_type!(pub BareRecordKey);
|
||||
// Untyped route id (hashed to 32 bytes)
|
||||
byte_array_type!(pub BareRouteId);
|
||||
// Untyped node id (hashed to 32 bytes)
|
||||
byte_array_type!(pub BareNodeId);
|
||||
// Untyped member id (hashed to 32 bytes)
|
||||
byte_array_type!(pub BareMemberId);
|
||||
// Untyped nonce (random 24 bytes, no typed variant)
|
||||
byte_array_type!(pub Nonce);
|
||||
|
||||
/*
|
||||
Notes:
|
||||
- These are actually BareHashDigest types, but not interchangable:
|
||||
- BareSharedSecret
|
||||
- BareRecordKey
|
||||
- BareRouteId (eventually will be a BareRecordKey type with DHT Routes)
|
||||
- BareNodeId (constructible from BarePublicKey)
|
||||
- BareMemberId (constructible from BarePublicKey)
|
||||
*/
|
||||
|
||||
// BareHashDigest sub-types
|
||||
byte_array_type!(BareHashDigest);
|
||||
byte_array_type!(BareSharedSecret);
|
||||
byte_array_type!(BareRecordKey);
|
||||
byte_array_type!(BareHashDistance);
|
||||
byte_array_type!(BareRouteId);
|
||||
byte_array_type!(BareNodeId);
|
||||
byte_array_type!(BareMemberId);
|
||||
|
||||
// Temporary adapters for converting to/from BareHashDigest types
|
||||
// Removing these will show where there's still issues.
|
||||
impl From<BareHashDigest> for BareSharedSecret {
|
||||
fn from(value: BareHashDigest) -> Self {
|
||||
Self::new(value.bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BareHashDigest> for BareRecordKey {
|
||||
fn from(value: BareHashDigest) -> Self {
|
||||
Self::new(value.bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BareRecordKey> for BareHashDigest {
|
||||
fn from(value: BareRecordKey) -> Self {
|
||||
Self::new(value.bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BareNodeId> for BareHashDigest {
|
||||
fn from(value: BareNodeId) -> Self {
|
||||
Self::new(value.bytes())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
- BareNodeId currently equals BarePublicKey, but should be distinct from BarePublicKey.
|
||||
- BareNodeId eventually should be a BareHashDigest type that's constructable from a BarePublicKey
|
||||
*/
|
||||
impl From<BarePublicKey> for BareNodeId {
|
||||
fn from(value: BarePublicKey) -> Self {
|
||||
Self::new(value.bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BareNodeId> for BarePublicKey {
|
||||
fn from(value: BareNodeId) -> Self {
|
||||
Self::new(value.bytes())
|
||||
}
|
||||
}
|
||||
// Internal types
|
||||
byte_array_type!(pub(crate) BareHashCoordinate);
|
||||
byte_array_type!(pub(crate) HashDistance);
|
||||
|
|
|
@ -1,209 +1,191 @@
|
|||
use super::*;
|
||||
#[macro_export]
|
||||
macro_rules! impl_crypto_typed {
|
||||
($visibility:vis $name:ident) => {
|
||||
paste::paste! {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[must_use]
|
||||
pub struct CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
{
|
||||
kind: CryptoKind,
|
||||
value: K,
|
||||
}
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(wasm_bindgen_derive::TryFromJsValue))]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[must_use]
|
||||
$visibility struct $name
|
||||
{
|
||||
kind: CryptoKind,
|
||||
value: [<Bare $name>],
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const CRYPOTYPED_TYPE: &'static str = r#"
|
||||
export type CryptoTyped<TCryptoKey extends string> = `${CryptoKind}:${TCryptoKey}`;
|
||||
"#;
|
||||
}
|
||||
}
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
make_wasm_bindgen_stubs!($name);
|
||||
|
||||
impl<K> CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
{
|
||||
pub fn new(kind: CryptoKind, value: K) -> Self {
|
||||
Self { kind, value }
|
||||
}
|
||||
impl $name {
|
||||
pub fn new(kind: CryptoKind, value: [<Bare $name>]) -> Self {
|
||||
Self { kind, value }
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> CryptoKind {
|
||||
self.kind
|
||||
}
|
||||
pub fn value(&self) -> K {
|
||||
self.value.clone()
|
||||
}
|
||||
pub fn ref_value(&self) -> &K {
|
||||
&self.value
|
||||
}
|
||||
pub fn into_value(self) -> K {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
pub fn ref_value(&self) -> &[<Bare $name>] {
|
||||
&self.value
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn into_value(self) -> [<Bare $name>] {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> PartialOrd for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: Ord + PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
impl $name {
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter, unchecked_return_type = "CryptoKind"))]
|
||||
pub fn kind(&self) -> CryptoKind {
|
||||
self.kind
|
||||
}
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter))]
|
||||
#[allow(dead_code)]
|
||||
pub fn value(&self) -> [<Bare $name>] {
|
||||
self.value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> Ord for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: Ord + PartialOrd,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
let x = compare_crypto_kind(&self.kind, &other.kind);
|
||||
if x != cmp::Ordering::Equal {
|
||||
return x;
|
||||
impl PartialOrd for $name
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for $name
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
let x = compare_crypto_kind(&self.kind, &other.kind);
|
||||
if x != cmp::Ordering::Equal {
|
||||
return x;
|
||||
}
|
||||
self.value.cmp(&other.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for $name
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}:{}", self.kind, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for $name
|
||||
{
|
||||
type Err = VeilidAPIError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let b = s.as_bytes();
|
||||
if b.len() > 5 && b[4..5] == b":"[..] {
|
||||
let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert");
|
||||
let value = [<Bare $name>]::try_decode_bytes(&b[5..])?;
|
||||
Ok(Self { kind, value })
|
||||
} else {
|
||||
let kind = best_crypto_kind();
|
||||
let value = [<Bare $name>]::try_decode_bytes(b)?;
|
||||
Ok(Self { kind, value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for $name
|
||||
{
|
||||
type Error = VeilidAPIError;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
Self::from_str(&s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for $name
|
||||
{
|
||||
type Error = VeilidAPIError;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
Self::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for $name
|
||||
{
|
||||
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 Serialize for $name
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.collect_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
#[wasm_bindgen]
|
||||
impl $name {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn js_new(kind: CryptoKind, value: [<Bare $name>]) -> Self {
|
||||
Self::new(kind,value)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = parse)]
|
||||
pub fn js_parse(s: String) -> VeilidAPIResult<Self> {
|
||||
Self::from_str(&s)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toString)]
|
||||
pub fn js_to_string(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = isEqual)]
|
||||
pub fn js_is_equal(&self, other: &Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
// TODO: add more typescript-only operations here
|
||||
}
|
||||
}
|
||||
self.value.cmp(&other.value)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<K> fmt::Display for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}:{}", self.kind, self.value)
|
||||
}
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! impl_crypto_typed_vec {
|
||||
($visibility:vis $name:ident) => {
|
||||
paste::paste! {
|
||||
impl<'a> TryFrom<&'a [u8]> for $name
|
||||
{
|
||||
type Error = VeilidAPIError;
|
||||
|
||||
impl<K> FromStr for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: Encodable,
|
||||
{
|
||||
type Err = VeilidAPIError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let b = s.as_bytes();
|
||||
if b.len() > 5 && b[4..5] == b":"[..] {
|
||||
let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert");
|
||||
let value = K::try_decode_bytes(&b[5..])?;
|
||||
Ok(Self { kind, value })
|
||||
} else {
|
||||
let kind = best_crypto_kind();
|
||||
let value = K::try_decode_bytes(b)?;
|
||||
Ok(Self { kind, value })
|
||||
fn try_from(b: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
if b.len() < 4 {
|
||||
apibail_generic!("invalid cryptotyped format");
|
||||
}
|
||||
let kind: CryptoKind = b[0..4].try_into()?;
|
||||
let value: [<Bare $name>] = b[4..].into();
|
||||
Ok(Self { kind, value })
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for $name
|
||||
{
|
||||
type Error = VeilidAPIError;
|
||||
|
||||
fn try_from(b: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Self::try_from(b.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name> for Vec<u8>
|
||||
{
|
||||
fn from(v: $name) -> Self {
|
||||
let mut out = v.kind.0.to_vec();
|
||||
out.extend_from_slice(v.value.as_ref());
|
||||
out
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> TryFrom<String> for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: Encodable,
|
||||
{
|
||||
type Error = VeilidAPIError;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
Self::from_str(&s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> TryFrom<&str> for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: Encodable,
|
||||
{
|
||||
type Error = VeilidAPIError;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
Self::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K> TryFrom<&'a [u8]> for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: From<&'a [u8]>,
|
||||
{
|
||||
type Error = VeilidAPIError;
|
||||
|
||||
fn try_from(b: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
if b.len() < 4 {
|
||||
apibail_generic!("invalid cryptotyped format");
|
||||
}
|
||||
let kind: CryptoKind = b[0..4].try_into()?;
|
||||
let value: K = b[4..].into();
|
||||
Ok(Self { kind, value })
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> TryFrom<Vec<u8>> for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: for<'a> From<&'a [u8]>,
|
||||
{
|
||||
type Error = VeilidAPIError;
|
||||
|
||||
fn try_from(b: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Self::try_from(b.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> From<CryptoTyped<K>> for Vec<u8>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: AsRef<[u8]>,
|
||||
{
|
||||
fn from(v: CryptoTyped<K>) -> Self {
|
||||
let mut out = v.kind.0.to_vec();
|
||||
out.extend_from_slice(v.value.as_ref());
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, K> Deserialize<'de> for CryptoTyped<K>
|
||||
where
|
||||
K: Clone + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: Encodable,
|
||||
{
|
||||
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 + fmt::Debug + PartialEq + Eq + Hash,
|
||||
K: fmt::Display,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.collect_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoTyped<BareKeyPair> {
|
||||
pub fn new_from_parts(key: PublicKey, bare_secret: BareSecretKey) -> Self {
|
||||
Self {
|
||||
kind: key.kind(),
|
||||
value: BareKeyPair::new(key.value(), bare_secret),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key(&self) -> PublicKey {
|
||||
PublicKey::new(self.kind, self.ref_value().key())
|
||||
}
|
||||
pub fn secret(&self) -> SecretKey {
|
||||
SecretKey::new(self.kind, self.ref_value().secret())
|
||||
}
|
||||
pub fn bare_secret(&self) -> BareSecretKey {
|
||||
self.ref_value().secret()
|
||||
}
|
||||
pub fn ref_bare_secret(&self) -> &BareSecretKey {
|
||||
self.ref_value().ref_secret()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,301 +1,282 @@
|
|||
use super::*;
|
||||
#[macro_export]
|
||||
macro_rules! impl_crypto_typed_group {
|
||||
($visibility:vis $name:ident) => {
|
||||
paste::paste! {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash, Default)]
|
||||
#[serde(from = "Vec<CryptoTyped<K>>", into = "Vec<CryptoTyped<K>>")]
|
||||
pub struct CryptoTypedGroup<K>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
items: Vec<CryptoTyped<K>>,
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const CRYPOTYPEDGROUP_TYPE: &'static str = r#"
|
||||
export type CryptoTypedGroup<TCryptoKey extends string> = Array<CryptoTyped<TCryptoKey>>;
|
||||
"#;
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> CryptoTypedGroup<K>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
#[must_use]
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self {
|
||||
items: Vec::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
pub fn kinds(&self) -> Vec<CryptoKind> {
|
||||
let mut out = Vec::new();
|
||||
for tk in &self.items {
|
||||
out.push(tk.kind());
|
||||
}
|
||||
out.sort_by(compare_crypto_kind);
|
||||
out
|
||||
}
|
||||
#[must_use]
|
||||
pub fn keys(&self) -> Vec<K> {
|
||||
let mut out = Vec::new();
|
||||
for tk in &self.items {
|
||||
out.push(tk.value());
|
||||
}
|
||||
out
|
||||
}
|
||||
#[must_use]
|
||||
pub fn get(&self, kind: CryptoKind) -> Option<CryptoTyped<K>> {
|
||||
self.items.iter().find(|x| x.kind() == kind).cloned()
|
||||
}
|
||||
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;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), derive(wasm_bindgen_derive::TryFromJsValue))]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash, Default)]
|
||||
#[serde(from = "Vec<_>", into = "Vec<_>")]
|
||||
pub struct [<$name Group>]
|
||||
{
|
||||
items: Vec<$name>,
|
||||
}
|
||||
}
|
||||
self.items.push(typed_key);
|
||||
self.items.sort()
|
||||
}
|
||||
pub fn add_all(&mut self, typed_keys: &[CryptoTyped<K>]) {
|
||||
'outer: for typed_key in typed_keys {
|
||||
for x in &mut self.items {
|
||||
if x.kind() == typed_key.kind() {
|
||||
*x = typed_key.clone();
|
||||
continue 'outer;
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
make_wasm_bindgen_stubs!([<$name Group>]);
|
||||
|
||||
impl [<$name Group>]
|
||||
{
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
#[must_use]
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self {
|
||||
items: Vec::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> core::slice::Iter<'_, $name> {
|
||||
self.items.iter()
|
||||
}
|
||||
|
||||
pub fn add_all_from_slice(&mut self, typed_keys: &[$name]) {
|
||||
'outer: for typed_key in typed_keys {
|
||||
for x in &mut self.items {
|
||||
if x.kind() == typed_key.kind() {
|
||||
*x = typed_key.clone();
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
self.items.push(typed_key.clone());
|
||||
}
|
||||
self.items.sort()
|
||||
}
|
||||
|
||||
pub fn contains_any_from_slice(&self, typed_keys: &[$name]) -> bool {
|
||||
for typed_key in typed_keys {
|
||||
if self.items.contains(typed_key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
#[wasm_bindgen]
|
||||
impl [<$name Group>]
|
||||
{
|
||||
#[must_use]
|
||||
pub fn get(&self,
|
||||
#[wasm_bindgen(unchecked_param_type = "CryptoKind")]
|
||||
kind: CryptoKind) -> Option<$name> {
|
||||
self.items.iter().find(|x| x.kind() == kind).cloned()
|
||||
}
|
||||
|
||||
pub fn remove(&mut self,
|
||||
#[wasm_bindgen(unchecked_param_type = "CryptoKind")]
|
||||
kind: CryptoKind) -> Option<$name> {
|
||||
if let Some(idx) = self.items.iter().position(|x| x.kind() == kind) {
|
||||
return Some(self.items.remove(idx));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self,
|
||||
#[wasm_bindgen(unchecked_param_type = "CryptoKind[]")]
|
||||
kinds: Vec<CryptoKind>) {
|
||||
for k in kinds {
|
||||
self.remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.items.push(typed_key.clone());
|
||||
}
|
||||
self.items.sort()
|
||||
}
|
||||
pub fn remove(&mut self, kind: CryptoKind) -> Option<CryptoTyped<K>> {
|
||||
if let Some(idx) = self.items.iter().position(|x| x.kind() == kind) {
|
||||
return Some(self.items.remove(idx));
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn remove_all(&mut self, kinds: &[CryptoKind]) {
|
||||
for k in kinds {
|
||||
self.remove(*k);
|
||||
}
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.items.clear();
|
||||
}
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
impl [<$name Group>]
|
||||
{
|
||||
#[must_use]
|
||||
pub fn get(&self, kind: CryptoKind) -> Option<$name> {
|
||||
self.items.iter().find(|x| x.kind() == kind).cloned()
|
||||
}
|
||||
|
||||
/// Return preferred typed key of our supported crypto kinds
|
||||
#[must_use]
|
||||
pub fn best(&self) -> Option<CryptoTyped<K>> {
|
||||
self.items
|
||||
.iter()
|
||||
.find(|k| VALID_CRYPTO_KINDS.contains(&k.kind()))
|
||||
.cloned()
|
||||
}
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.items.is_empty()
|
||||
}
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
pub fn iter(&self) -> core::slice::Iter<'_, CryptoTyped<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;
|
||||
pub fn remove(&mut self, kind: CryptoKind) -> Option<$name> {
|
||||
if let Some(idx) = self.items.iter().position(|x| x.kind() == kind) {
|
||||
return Some(self.items.remove(idx));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self, kinds: Vec<CryptoKind>) {
|
||||
for k in kinds {
|
||||
self.remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
impl [<$name Group>] {
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter, unchecked_return_type = "CryptoKind[]"))]
|
||||
pub fn kinds(&self) -> Vec<CryptoKind> {
|
||||
let mut out = Vec::new();
|
||||
for tk in &self.items {
|
||||
out.push(tk.kind());
|
||||
}
|
||||
out.sort_by(compare_crypto_kind);
|
||||
out
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn keys(&self) -> Vec<[<Bare $name>]> {
|
||||
let mut out = Vec::<[<Bare $name>]>::new();
|
||||
for tk in &self.items {
|
||||
out.push(tk.value());
|
||||
}
|
||||
out
|
||||
}
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.items.is_empty()
|
||||
}
|
||||
#[must_use]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(getter, js_name = length))]
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
pub fn contains(&self, typed_key: &$name) -> bool {
|
||||
self.items.contains(typed_key)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn contains_any(&self, typed_keys: Vec<$name>) -> bool {
|
||||
self.contains_any_from_slice(&typed_keys)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen(js_name = toArray))]
|
||||
pub fn to_vec(&self) -> Vec<$name> {
|
||||
self.items.clone()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, typed_key: $name) {
|
||||
for x in &mut self.items {
|
||||
if x.kind() == typed_key.kind() {
|
||||
*x = typed_key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.items.push(typed_key);
|
||||
self.items.sort()
|
||||
}
|
||||
|
||||
pub fn add_all(&mut self, typed_keys: Vec<$name>) {
|
||||
self.add_all_from_slice(&typed_keys)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.items.clear();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl core::ops::Deref for [<$name Group>]
|
||||
{
|
||||
type Target = [$name];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[$name] {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for [<$name Group>]
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "[")?;
|
||||
let mut first = true;
|
||||
for x in &self.items {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
write!(f, "{}", x)?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
impl FromStr for [<$name Group>]
|
||||
{
|
||||
type Err = VeilidAPIError;
|
||||
fn from_str(s: &str) -> Result<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 = $name::from_str(x.trim())?;
|
||||
items.push(tk);
|
||||
}
|
||||
|
||||
Ok(Self { items })
|
||||
}
|
||||
}
|
||||
impl From<$name> for [<$name Group>]
|
||||
{
|
||||
fn from(x: $name) -> Self {
|
||||
let mut tks = [<$name Group>]::with_capacity(1);
|
||||
tks.add(x);
|
||||
tks
|
||||
}
|
||||
}
|
||||
impl From<Vec<$name>> for [<$name Group>]
|
||||
{
|
||||
fn from(x: Vec<$name>) -> Self {
|
||||
let mut tks = [<$name Group>]::with_capacity(x.len());
|
||||
tks.add_all_from_slice(&x);
|
||||
tks
|
||||
}
|
||||
}
|
||||
impl From<&[$name]> for [<$name Group>]
|
||||
{
|
||||
fn from(x: &[$name]) -> Self {
|
||||
let mut tks = [<$name Group>]::with_capacity(x.len());
|
||||
tks.add_all_from_slice(x);
|
||||
tks
|
||||
}
|
||||
}
|
||||
impl From<[<$name Group>]> for Vec<$name>
|
||||
{
|
||||
fn from(val: [<$name Group>]) -> Self {
|
||||
val.items
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
#[wasm_bindgen]
|
||||
impl [<$name Group>] {
|
||||
#[wasm_bindgen(constructor)]
|
||||
#[must_use]
|
||||
pub fn js_new() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = parse)]
|
||||
pub fn js_parse(s: String) -> VeilidAPIResult<Self> {
|
||||
Self::from_str(&s)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toString)]
|
||||
#[must_use]
|
||||
pub fn js_to_string(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = isEqual)]
|
||||
#[must_use]
|
||||
pub fn js_is_equal(&self, other: &Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
|
||||
// TODO: add more typescript-only operations here
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
pub fn contains_value(&self, value: &K) -> bool {
|
||||
for tk in &self.items {
|
||||
if tk.ref_value() == value {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> core::ops::Deref for CryptoTypedGroup<K>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
type Target = [CryptoTyped<K>];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[CryptoTyped<K>] {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> fmt::Display for CryptoTypedGroup<K>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "[")?;
|
||||
let mut first = true;
|
||||
for x in &self.items {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
write!(f, "{}", x)?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
impl<K> FromStr for CryptoTypedGroup<K>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
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 CryptoTypedGroup<K>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
fn from(x: CryptoTyped<K>) -> Self {
|
||||
let mut tks = CryptoTypedGroup::<K>::with_capacity(1);
|
||||
tks.add(x);
|
||||
tks
|
||||
}
|
||||
}
|
||||
impl<K> From<Vec<CryptoTyped<K>>> for CryptoTypedGroup<K>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
fn from(x: Vec<CryptoTyped<K>>) -> Self {
|
||||
let mut tks = CryptoTypedGroup::<K>::with_capacity(x.len());
|
||||
tks.add_all(&x);
|
||||
tks
|
||||
}
|
||||
}
|
||||
impl<K> From<&[CryptoTyped<K>]> for CryptoTypedGroup<K>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
fn from(x: &[CryptoTyped<K>]) -> Self {
|
||||
let mut tks = CryptoTypedGroup::<K>::with_capacity(x.len());
|
||||
tks.add_all(x);
|
||||
tks
|
||||
}
|
||||
}
|
||||
impl<K> From<CryptoTypedGroup<K>> for Vec<CryptoTyped<K>>
|
||||
where
|
||||
K: Clone
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ FromStr
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ PartialOrd
|
||||
+ Ord
|
||||
+ Hash
|
||||
+ Encodable,
|
||||
{
|
||||
fn from(val: CryptoTypedGroup<K>) -> Self {
|
||||
val.items
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,56 +1,66 @@
|
|||
use super::*;
|
||||
|
||||
#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
derive(wasm_bindgen_derive::TryFromJsValue)
|
||||
)]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
#[derive(Clone, Default, Hash, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[must_use]
|
||||
pub struct BareKeyPair {
|
||||
key: BarePublicKey,
|
||||
secret: BareSecretKey,
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const KEYPAIR_TYPE: &'static str = r#"
|
||||
export type BareKeyPair = `${BarePublicKey}:${BareSecretKey}`;
|
||||
"#;
|
||||
}
|
||||
}
|
||||
|
||||
impl BareKeyPair {
|
||||
pub fn new(key: BarePublicKey, secret: BareSecretKey) -> Self {
|
||||
Self { key, secret }
|
||||
}
|
||||
pub fn key(&self) -> BarePublicKey {
|
||||
self.key.clone()
|
||||
}
|
||||
pub fn secret(&self) -> BareSecretKey {
|
||||
self.secret.clone()
|
||||
}
|
||||
pub fn ref_key(&self) -> &BarePublicKey {
|
||||
&self.key
|
||||
}
|
||||
pub fn ref_secret(&self) -> &BareSecretKey {
|
||||
&self.secret
|
||||
}
|
||||
pub fn split(&self) -> (BarePublicKey, BareSecretKey) {
|
||||
(self.key.clone(), self.secret.clone())
|
||||
}
|
||||
pub fn ref_split(&self) -> (&BarePublicKey, &BareSecretKey) {
|
||||
(&self.key, &self.secret)
|
||||
}
|
||||
pub fn split(&self) -> (BarePublicKey, BareSecretKey) {
|
||||
(self.key.clone(), self.secret.clone())
|
||||
}
|
||||
pub fn into_split(self) -> (BarePublicKey, BareSecretKey) {
|
||||
(self.key, self.secret)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for BareKeyPair {
|
||||
fn encode(&self) -> String {
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
#[allow(dead_code)]
|
||||
impl BareKeyPair {
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
wasm_bindgen(getter)
|
||||
)]
|
||||
pub fn key(&self) -> BarePublicKey {
|
||||
self.key.clone()
|
||||
}
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
wasm_bindgen(getter)
|
||||
)]
|
||||
pub fn secret(&self) -> BareSecretKey {
|
||||
self.secret.clone()
|
||||
}
|
||||
pub fn encode(&self) -> String {
|
||||
format!("{}:{}", self.key.encode(), self.secret.encode())
|
||||
}
|
||||
fn encoded_len(&self) -> usize {
|
||||
pub fn encoded_len(&self) -> usize {
|
||||
self.key.encoded_len() + 1 + self.secret.encoded_len()
|
||||
}
|
||||
fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult<Self> {
|
||||
pub fn try_decode(input: &str) -> VeilidAPIResult<Self> {
|
||||
let b = input.as_bytes();
|
||||
Self::try_decode_bytes(b)
|
||||
}
|
||||
pub fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult<Self> {
|
||||
let parts: Vec<_> = b.split(|x| *x == b':').collect();
|
||||
if parts.len() != 2 {
|
||||
apibail_parse_error!(
|
||||
|
@ -63,6 +73,7 @@ impl Encodable for BareKeyPair {
|
|||
Ok(BareKeyPair { key, secret })
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BareKeyPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.encode())
|
||||
|
@ -125,3 +136,50 @@ impl<'de> serde::Deserialize<'de> for BareKeyPair {
|
|||
BareKeyPair::try_decode(s.as_str()).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl KeyPair {
|
||||
pub fn into_split(self) -> (PublicKey, SecretKey) {
|
||||
let kind = self.kind;
|
||||
let (pk, sk) = self.into_value().into_split();
|
||||
(PublicKey::new(kind, pk), SecretKey::new(kind, sk))
|
||||
}
|
||||
|
||||
pub fn ref_bare_secret(&self) -> &BareSecretKey {
|
||||
self.ref_value().ref_secret()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen)]
|
||||
#[allow(dead_code)]
|
||||
impl KeyPair {
|
||||
pub fn new_from_parts(key: PublicKey, bare_secret: BareSecretKey) -> Self {
|
||||
Self {
|
||||
kind: key.kind(),
|
||||
value: BareKeyPair::new(key.value(), bare_secret),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
wasm_bindgen(getter)
|
||||
)]
|
||||
pub fn key(&self) -> PublicKey {
|
||||
PublicKey::new(self.kind, self.ref_value().key())
|
||||
}
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
wasm_bindgen(getter)
|
||||
)]
|
||||
pub fn secret(&self) -> SecretKey {
|
||||
SecretKey::new(self.kind, self.ref_value().secret())
|
||||
}
|
||||
#[cfg_attr(
|
||||
all(target_arch = "wasm32", target_os = "unknown"),
|
||||
wasm_bindgen(getter)
|
||||
)]
|
||||
pub fn bare_secret(&self) -> BareSecretKey {
|
||||
self.ref_value().secret()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,94 +48,38 @@ mod crypto_typed_group;
|
|||
mod keypair;
|
||||
|
||||
pub use byte_array_types::*;
|
||||
pub use crypto_typed::*;
|
||||
pub use crypto_typed_group::*;
|
||||
pub use keypair::*;
|
||||
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type EncapsulationKey = CryptoTyped<BareEncapsulationKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type DecapsulationKey = CryptoTyped<BareDecapsulationKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type PublicKey = CryptoTyped<BarePublicKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type SecretKey = CryptoTyped<BareSecretKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type KeyPair = CryptoTyped<BareKeyPair>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type Signature = CryptoTyped<BareSignature>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type SharedSecret = CryptoTyped<BareSharedSecret>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type HashDigest = CryptoTyped<BareHashDigest>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type RecordKey = CryptoTyped<BareRecordKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type NodeId = CryptoTyped<BareNodeId>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type RouteId = CryptoTyped<BareRouteId>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type MemberId = CryptoTyped<BareMemberId>;
|
||||
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type EncapsulationKeyGroup = CryptoTypedGroup<BareEncapsulationKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type DecapsulationKeyGroup = CryptoTypedGroup<BareDecapsulationKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type PublicKeyGroup = CryptoTypedGroup<BarePublicKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type SecretKeyGroup = CryptoTypedGroup<BareSecretKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type KeyPairGroup = CryptoTypedGroup<BareKeyPair>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type SignatureGroup = CryptoTypedGroup<BareSignature>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type SharedSecretGroup = CryptoTypedGroup<BareSharedSecret>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type HashDigestGroup = CryptoTypedGroup<BareHashDigest>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type RecordKeyGroup = CryptoTypedGroup<BareRecordKey>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type NodeIdGroup = CryptoTypedGroup<BareNodeId>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type RouteIdGroup = CryptoTypedGroup<BareRouteId>;
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), declare)]
|
||||
pub type MemberIdGroup = CryptoTypedGroup<BareMemberId>;
|
||||
|
||||
impl From<NodeId> for HashDigest {
|
||||
fn from(value: NodeId) -> Self {
|
||||
HashDigest::new(value.kind(), value.into_value().into())
|
||||
}
|
||||
macro_rules! impl_crypto_typed_and_group {
|
||||
($visibility:vis $name:ident) => {
|
||||
impl_crypto_typed!($visibility $name);
|
||||
impl_crypto_typed_group!($visibility $name);
|
||||
};
|
||||
}
|
||||
|
||||
impl From<RecordKey> for HashDigest {
|
||||
fn from(value: RecordKey) -> Self {
|
||||
HashDigest::new(value.kind(), value.into_value().into())
|
||||
}
|
||||
macro_rules! impl_crypto_typed_and_group_and_vec {
|
||||
($visibility:vis $name:ident) => {
|
||||
impl_crypto_typed!($visibility $name);
|
||||
impl_crypto_typed_group!($visibility $name);
|
||||
impl_crypto_typed_vec!($visibility $name);
|
||||
};
|
||||
}
|
||||
|
||||
impl From<NodeId> for PublicKey {
|
||||
fn from(value: NodeId) -> Self {
|
||||
PublicKey::new(value.kind(), value.into_value().into())
|
||||
}
|
||||
}
|
||||
// CryptoKind typed, with group and vector conversions
|
||||
impl_crypto_typed_and_group_and_vec!(pub EncapsulationKey);
|
||||
impl_crypto_typed_and_group_and_vec!(pub DecapsulationKey);
|
||||
impl_crypto_typed_and_group_and_vec!(pub PublicKey);
|
||||
impl_crypto_typed_and_group_and_vec!(pub SecretKey);
|
||||
impl_crypto_typed_and_group_and_vec!(pub Signature);
|
||||
impl_crypto_typed_and_group_and_vec!(pub SharedSecret);
|
||||
impl_crypto_typed_and_group_and_vec!(pub HashDigest);
|
||||
impl_crypto_typed_and_group_and_vec!(pub RecordKey);
|
||||
impl_crypto_typed_and_group_and_vec!(pub NodeId);
|
||||
impl_crypto_typed_and_group_and_vec!(pub RouteId);
|
||||
impl_crypto_typed_and_group_and_vec!(pub MemberId);
|
||||
|
||||
impl From<PublicKey> for NodeId {
|
||||
fn from(value: PublicKey) -> Self {
|
||||
NodeId::new(value.kind(), value.into_value().into())
|
||||
}
|
||||
}
|
||||
// No vector representation
|
||||
impl_crypto_typed_and_group!(pub KeyPair);
|
||||
|
||||
impl From<NodeIdGroup> for PublicKeyGroup {
|
||||
fn from(value: NodeIdGroup) -> Self {
|
||||
let items: Vec<PublicKey> = value.iter().map(|node_id| node_id.clone().into()).collect();
|
||||
PublicKeyGroup::from(items)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublicKeyGroup> for NodeIdGroup {
|
||||
fn from(value: PublicKeyGroup) -> Self {
|
||||
let items: Vec<NodeId> = value.iter().map(|node_id| node_id.clone().into()).collect();
|
||||
NodeIdGroup::from(items)
|
||||
}
|
||||
}
|
||||
// Internal types
|
||||
impl_crypto_typed!(pub(crate) HashCoordinate);
|
||||
|
|
|
@ -49,7 +49,7 @@ impl BootstrapRecord {
|
|||
}
|
||||
|
||||
pub fn merge(&mut self, other: BootstrapRecord) {
|
||||
self.public_keys.add_all(&other.public_keys);
|
||||
self.public_keys.add_all_from_slice(&other.public_keys);
|
||||
for x in other.envelope_support {
|
||||
if !self.envelope_support.contains(&x) {
|
||||
self.envelope_support.push(x);
|
||||
|
@ -83,7 +83,7 @@ impl BootstrapRecord {
|
|||
.envelope_support()
|
||||
.iter()
|
||||
.map(|x| {
|
||||
if (x.0)[0..3] == *b"ENV" {
|
||||
if x.bytes()[0..3] == *b"ENV" {
|
||||
x.to_string().split_off(3)
|
||||
} else {
|
||||
x.to_string()
|
||||
|
@ -161,19 +161,20 @@ impl BootstrapRecord {
|
|||
|
||||
let crypto = network_manager.crypto();
|
||||
|
||||
let sig = match crypto.generate_signatures(v1.as_bytes(), &[signing_key_pair], |kp, sig| {
|
||||
Signature::new(kp.kind(), sig).to_string()
|
||||
}) {
|
||||
Ok(v) => {
|
||||
let Some(sig) = v.first().cloned() else {
|
||||
bail!("No signature generated");
|
||||
};
|
||||
sig
|
||||
}
|
||||
Err(e) => {
|
||||
bail!("Failed to generate signature: {}", e);
|
||||
}
|
||||
};
|
||||
let sig =
|
||||
match crypto.generate_signatures(v1.as_bytes(), &[signing_key_pair], |_kp, sig| {
|
||||
sig.to_string()
|
||||
}) {
|
||||
Ok(v) => {
|
||||
let Some(sig) = v.first().cloned() else {
|
||||
bail!("No signature generated");
|
||||
};
|
||||
sig
|
||||
}
|
||||
Err(e) => {
|
||||
bail!("Failed to generate signature: {}", e);
|
||||
}
|
||||
};
|
||||
|
||||
v1 += &sig;
|
||||
Ok(v1)
|
||||
|
|
|
@ -112,7 +112,7 @@ impl NetworkManager {
|
|||
// and as such, a routing domain can not be determined for it
|
||||
// by the code that receives the FindNodeA result
|
||||
for pi in bootv1response.peers.iter().cloned() {
|
||||
if pi.node_info().public_keys().contains_any(bsrec.public_keys()) {
|
||||
if pi.node_info().public_keys().contains_any_from_slice(bsrec.public_keys()) {
|
||||
return Some(pi);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,8 +75,8 @@ impl NetworkManager {
|
|||
.filter_map(|bsrec| {
|
||||
if routing_table.matches_own_public_key(bsrec.public_keys()) {
|
||||
routing_table.get_published_peer_info(routing_domain)
|
||||
} else if let Some(best_public_key) = bsrec.public_keys().best() {
|
||||
if let Ok(best_node_id) = routing_table.generate_node_id(&best_public_key) {
|
||||
} else if let Some(best_public_key) = bsrec.public_keys().first() {
|
||||
if let Ok(best_node_id) = routing_table.generate_node_id(best_public_key) {
|
||||
if let Some(nr) = routing_table.lookup_node_ref(best_node_id).ok().flatten()
|
||||
{
|
||||
nr.get_peer_info(routing_domain)
|
||||
|
|
|
@ -104,7 +104,10 @@ impl NetworkManager {
|
|||
let mut mbi = 0;
|
||||
while mbi < merged_bootstrap_records.len() {
|
||||
let mbr = &mut merged_bootstrap_records[mbi];
|
||||
if mbr.public_keys().contains_any(bsrec.public_keys()) {
|
||||
if mbr
|
||||
.public_keys()
|
||||
.contains_any_from_slice(bsrec.public_keys())
|
||||
{
|
||||
// Merge record, pop this one out
|
||||
let mbr = merged_bootstrap_records.remove(mbi);
|
||||
bsrec.merge(mbr);
|
||||
|
|
|
@ -44,7 +44,10 @@ impl NetworkManager {
|
|||
let mut mbi = 0;
|
||||
while mbi < merged_bootstrap_records.len() {
|
||||
let mbr = &mut merged_bootstrap_records[mbi];
|
||||
if mbr.public_keys().contains_any(bsrec.public_keys()) {
|
||||
if mbr
|
||||
.public_keys()
|
||||
.contains_any_from_slice(bsrec.public_keys())
|
||||
{
|
||||
// Merge record, pop this one out
|
||||
let mbr = merged_bootstrap_records.remove(mbi);
|
||||
bsrec.merge(mbr);
|
||||
|
|
|
@ -67,7 +67,7 @@ pub const TXT_LOOKUP_CACHE_SIZE: usize = 256;
|
|||
/// Duration that TXT lookups are valid in the cache (5 minutes, <= the DNS record expiration timeout)
|
||||
pub const TXT_LOOKUP_EXPIRATION: TimestampDuration = TimestampDuration::new_secs(300);
|
||||
/// Maximum size for a message is the same as the maximum size for an Envelope
|
||||
pub const MAX_MESSAGE_SIZE: usize = MAX_ENVELOPE_SIZE;
|
||||
pub const MAX_MESSAGE_SIZE: usize = ENV0_MAX_ENVELOPE_SIZE;
|
||||
/// Statistics table size for tracking performance by IP address
|
||||
pub const IPADDR_TABLE_SIZE: usize = 1024;
|
||||
/// Eviction time for ip addresses from statistics tables (5 minutes)
|
||||
|
@ -111,8 +111,8 @@ impl SendDataResult {
|
|||
Some(ncm) if ncm.is_direct()
|
||||
)
|
||||
}
|
||||
pub fn is_ordered(&self) -> bool {
|
||||
self.unique_flow.flow.protocol_type().is_ordered()
|
||||
pub fn sequence_ordering(&self) -> SequenceOrdering {
|
||||
self.unique_flow.flow.protocol_type().sequence_ordering()
|
||||
}
|
||||
|
||||
pub fn unique_flow(&self) -> UniqueFlow {
|
||||
|
@ -286,9 +286,11 @@ impl NetworkManager {
|
|||
Some(
|
||||
bcs.derive_shared_secret(
|
||||
network_key_password.as_bytes(),
|
||||
&bcs.generate_hash(network_key_password.as_bytes()),
|
||||
bcs.generate_hash(network_key_password.as_bytes())
|
||||
.ref_value(),
|
||||
)
|
||||
.expect("failed to derive network key"),
|
||||
.expect("failed to derive network key")
|
||||
.value(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
|
@ -593,6 +595,7 @@ impl NetworkManager {
|
|||
|
||||
/// Generates a multi-shot/normal receipt
|
||||
#[instrument(level = "trace", skip(self, extra_data, callback))]
|
||||
#[expect(dead_code)]
|
||||
pub fn generate_receipt<D: AsRef<[u8]>>(
|
||||
&self,
|
||||
expiration_us: TimestampDuration,
|
||||
|
@ -612,15 +615,19 @@ impl NetworkManager {
|
|||
|
||||
let nonce = vcrypto.random_nonce();
|
||||
let node_id = routing_table.node_id(vcrypto.kind());
|
||||
let secret_key = routing_table.secret_key(vcrypto.kind()).value();
|
||||
let secret_key = routing_table.secret_key(vcrypto.kind());
|
||||
|
||||
// Encode envelope
|
||||
let version = best_receipt_version();
|
||||
let receipt = match version {
|
||||
RECEIPT_VERSION_RCP0 => {
|
||||
Receipt::try_new_rcp0(&crypto, node_id.kind(), nonce, node_id, extra_data)?
|
||||
}
|
||||
_ => {
|
||||
bail!("unsupported receipt version: {:?}", version);
|
||||
}
|
||||
};
|
||||
|
||||
let receipt = Receipt::try_new(
|
||||
best_receipt_version(),
|
||||
node_id.kind(),
|
||||
nonce,
|
||||
node_id.value(),
|
||||
extra_data,
|
||||
)?;
|
||||
let out = receipt
|
||||
.to_signed_data(&crypto, &secret_key)
|
||||
.wrap_err("failed to generate signed receipt")?;
|
||||
|
@ -652,15 +659,19 @@ impl NetworkManager {
|
|||
|
||||
let nonce = vcrypto.random_nonce();
|
||||
let node_id = routing_table.node_id(vcrypto.kind());
|
||||
let secret_key = routing_table.secret_key(vcrypto.kind()).value();
|
||||
let secret_key = routing_table.secret_key(vcrypto.kind());
|
||||
|
||||
let version = best_receipt_version();
|
||||
|
||||
let receipt = match version {
|
||||
RECEIPT_VERSION_RCP0 => {
|
||||
Receipt::try_new_rcp0(&crypto, node_id.kind(), nonce, node_id, extra_data)?
|
||||
}
|
||||
_ => {
|
||||
bail!("unsupported receipt version: {:?}", version);
|
||||
}
|
||||
};
|
||||
|
||||
let receipt = Receipt::try_new(
|
||||
best_receipt_version(),
|
||||
node_id.kind(),
|
||||
nonce,
|
||||
node_id.value(),
|
||||
extra_data,
|
||||
)?;
|
||||
let out = receipt
|
||||
.to_signed_data(&crypto, &secret_key)
|
||||
.wrap_err("failed to generate signed receipt")?;
|
||||
|
@ -687,7 +698,7 @@ impl NetworkManager {
|
|||
let receipt_manager = self.receipt_manager();
|
||||
let crypto = self.crypto();
|
||||
|
||||
let receipt = match Receipt::from_signed_data(&crypto, receipt_data.as_ref()) {
|
||||
let receipt = match Receipt::try_from_signed_data(&crypto, receipt_data.as_ref()) {
|
||||
Err(e) => {
|
||||
return NetworkResult::invalid_message(e.to_string());
|
||||
}
|
||||
|
@ -713,7 +724,7 @@ impl NetworkManager {
|
|||
let receipt_manager = self.receipt_manager();
|
||||
let crypto = self.crypto();
|
||||
|
||||
let receipt = match Receipt::from_signed_data(&crypto, receipt_data.as_ref()) {
|
||||
let receipt = match Receipt::try_from_signed_data(&crypto, receipt_data.as_ref()) {
|
||||
Err(e) => {
|
||||
return NetworkResult::invalid_message(e.to_string());
|
||||
}
|
||||
|
@ -738,7 +749,7 @@ impl NetworkManager {
|
|||
let receipt_manager = self.receipt_manager();
|
||||
let crypto = self.crypto();
|
||||
|
||||
let receipt = match Receipt::from_signed_data(&crypto, receipt_data.as_ref()) {
|
||||
let receipt = match Receipt::try_from_signed_data(&crypto, receipt_data.as_ref()) {
|
||||
Err(e) => {
|
||||
return NetworkResult::invalid_message(e.to_string());
|
||||
}
|
||||
|
@ -755,7 +766,7 @@ impl NetworkManager {
|
|||
pub async fn handle_private_receipt<R: AsRef<[u8]>>(
|
||||
&self,
|
||||
receipt_data: R,
|
||||
private_route: BarePublicKey,
|
||||
private_route: PublicKey,
|
||||
) -> NetworkResult<()> {
|
||||
let Ok(_guard) = self.startup_context.startup_lock.enter() else {
|
||||
return NetworkResult::service_unavailable("network is not started");
|
||||
|
@ -764,7 +775,7 @@ impl NetworkManager {
|
|||
let receipt_manager = self.receipt_manager();
|
||||
let crypto = self.crypto();
|
||||
|
||||
let receipt = match Receipt::from_signed_data(&crypto, receipt_data.as_ref()) {
|
||||
let receipt = match Receipt::try_from_signed_data(&crypto, receipt_data.as_ref()) {
|
||||
Err(e) => {
|
||||
return NetworkResult::invalid_message(e.to_string());
|
||||
}
|
||||
|
@ -805,9 +816,11 @@ impl NetworkManager {
|
|||
};
|
||||
|
||||
// Restrict reverse connection to same sequencing requirement as inbound signal
|
||||
if signal_flow.protocol_type().is_ordered() {
|
||||
peer_nr.set_sequencing(Sequencing::EnsureOrdered);
|
||||
}
|
||||
let sequencing = signal_flow
|
||||
.protocol_type()
|
||||
.sequence_ordering()
|
||||
.strict_sequencing();
|
||||
peer_nr.set_sequencing(sequencing);
|
||||
|
||||
// Make a reverse connection to the peer and send the receipt to it
|
||||
rpc.rpc_call_return_receipt(Destination::direct(peer_nr), receipt)
|
||||
|
@ -886,21 +899,21 @@ impl NetworkManager {
|
|||
};
|
||||
|
||||
let node_id = routing_table.node_id(vcrypto.kind());
|
||||
let secret_key = routing_table.secret_key(vcrypto.kind()).value();
|
||||
let secret_key = routing_table.secret_key(vcrypto.kind());
|
||||
|
||||
// Get timestamp, nonce
|
||||
let ts = Timestamp::now();
|
||||
let nonce = vcrypto.random_nonce();
|
||||
|
||||
// Encode envelope
|
||||
let envelope = Envelope::new(
|
||||
version,
|
||||
node_id.kind(),
|
||||
ts,
|
||||
nonce,
|
||||
node_id.value(),
|
||||
dest_node_id.value(),
|
||||
);
|
||||
let envelope = match version {
|
||||
ENVELOPE_VERSION_ENV0 => {
|
||||
Envelope::try_new_env0(&crypto, node_id.kind(), ts, nonce, node_id, dest_node_id)?
|
||||
}
|
||||
_ => {
|
||||
bail!("unsupported envelope version: {:?}", version);
|
||||
}
|
||||
};
|
||||
envelope
|
||||
.to_encrypted_data(&crypto, body.as_ref(), &secret_key, &self.network_key)
|
||||
.wrap_err("envelope failed to encode")
|
||||
|
@ -1046,7 +1059,7 @@ impl NetworkManager {
|
|||
|
||||
// Decode envelope header (may fail signature validation)
|
||||
let crypto = self.crypto();
|
||||
let envelope = match Envelope::from_signed_data(&crypto, data, &self.network_key) {
|
||||
let envelope = match Envelope::try_from_signed_data(&crypto, data, &self.network_key) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
veilid_log!(self debug "envelope failed to decode: {}", e);
|
||||
|
@ -1164,9 +1177,8 @@ impl NetworkManager {
|
|||
if let Some((mut relay_nr, relay_kind)) = some_relay {
|
||||
// Ensure the protocol used to forward is of the same sequencing requirement
|
||||
// Address type is allowed to change if connectivity is better
|
||||
if flow.protocol_type().is_ordered() {
|
||||
relay_nr.set_sequencing(Sequencing::EnsureOrdered);
|
||||
};
|
||||
let sequencing = flow.protocol_type().sequence_ordering().strict_sequencing();
|
||||
relay_nr.set_sequencing(sequencing);
|
||||
|
||||
// Pass relay to RPC system
|
||||
if let Err(e) = self.enqueue_relay(relay_nr, data.to_vec(), relay_kind) {
|
||||
|
@ -1180,7 +1192,7 @@ impl NetworkManager {
|
|||
}
|
||||
|
||||
// DH to get decryption key (cached)
|
||||
let secret_key = routing_table.secret_key(envelope.get_crypto_kind()).value();
|
||||
let secret_key = routing_table.secret_key(envelope.get_crypto_kind());
|
||||
|
||||
// Decrypt the envelope body
|
||||
let crypto = self.crypto();
|
||||
|
@ -1257,7 +1269,12 @@ impl NetworkManager {
|
|||
|
||||
// Inform the connection table about the flow's priority
|
||||
let is_relaying_flow = node_ref.is_relaying(routing_domain);
|
||||
if is_relaying_flow && flow.protocol_type().is_ordered() {
|
||||
if is_relaying_flow
|
||||
&& matches!(
|
||||
flow.protocol_type().sequence_ordering(),
|
||||
SequenceOrdering::Ordered
|
||||
)
|
||||
{
|
||||
self.connection_manager().add_relaying_flow(flow);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ impl IGDManager {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", target = "net", skip_all)]
|
||||
#[expect(dead_code)]
|
||||
pub async fn unmap_port(
|
||||
&self,
|
||||
protocol_type: IGDProtocolType,
|
||||
|
|
|
@ -17,7 +17,7 @@ pub enum ReceiptEvent {
|
|||
ReturnedSafety,
|
||||
ReturnedPrivate {
|
||||
#[expect(dead_code)]
|
||||
private_route: BarePublicKey,
|
||||
private_route: PublicKey,
|
||||
},
|
||||
Expired,
|
||||
Cancelled,
|
||||
|
@ -28,7 +28,7 @@ pub(super) enum ReceiptReturned {
|
|||
OutOfBand,
|
||||
InBand { inbound_noderef: FilteredNodeRef },
|
||||
Safety,
|
||||
Private { private_route: BarePublicKey },
|
||||
Private { private_route: PublicKey },
|
||||
}
|
||||
|
||||
pub trait ReceiptCallback: Send + 'static {
|
||||
|
@ -86,7 +86,6 @@ struct ReceiptRecord {
|
|||
}
|
||||
|
||||
impl ReceiptRecord {
|
||||
#[expect(dead_code)]
|
||||
pub fn new(
|
||||
receipt: Receipt,
|
||||
expiration_ts: Timestamp,
|
||||
|
@ -145,7 +144,7 @@ impl PartialOrd for ReceiptRecordTimestampSort {
|
|||
///////////////////////////////////
|
||||
|
||||
struct ReceiptManagerInner {
|
||||
records_by_nonce: BTreeMap<BareNonce, Arc<Mutex<ReceiptRecord>>>,
|
||||
records_by_nonce: BTreeMap<Nonce, Arc<Mutex<ReceiptRecord>>>,
|
||||
next_oldest_ts: Option<Timestamp>,
|
||||
stop_source: Option<StopSource>,
|
||||
timeout_task: MustJoinSingleFuture<()>,
|
||||
|
@ -405,7 +404,7 @@ impl ReceiptManager {
|
|||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub async fn cancel_receipt(&self, nonce: &BareNonce) -> EyreResult<()> {
|
||||
pub async fn cancel_receipt(&self, nonce: &Nonce) -> EyreResult<()> {
|
||||
event!(target: "receipt", Level::DEBUG, "== Cancel Receipt {}", nonce.encode());
|
||||
|
||||
let _guard = self.unlocked_inner.startup_lock.enter()?;
|
||||
|
|
|
@ -691,13 +691,13 @@ impl NetworkManager {
|
|||
relay_nr.set_sequencing(sequencing);
|
||||
|
||||
// Tighten sequencing for the target to the best reverse connection flow we can get
|
||||
let tighten = peer_a
|
||||
let max_ordering = peer_a
|
||||
.node_info()
|
||||
.filtered_dial_info_details(DialInfoDetail::NO_SORT, &|did| {
|
||||
did.matches_filter(&dial_info_filter)
|
||||
})
|
||||
.iter()
|
||||
.find_map(|did| {
|
||||
.fold(SequenceOrdering::Unordered, |ord, did| {
|
||||
if peer_b
|
||||
.node_info()
|
||||
.address_types()
|
||||
|
@ -706,21 +706,17 @@ impl NetworkManager {
|
|||
.node_info()
|
||||
.outbound_protocols()
|
||||
.contains(did.dial_info.protocol_type())
|
||||
&& did.dial_info.protocol_type().is_ordered()
|
||||
{
|
||||
Some(true)
|
||||
cmp::max(ord, did.dial_info.protocol_type().sequence_ordering())
|
||||
} else {
|
||||
None
|
||||
ord
|
||||
}
|
||||
})
|
||||
.unwrap_or(false);
|
||||
});
|
||||
|
||||
let mut target_node_ref = target_node_ref.filtered_clone(
|
||||
NodeRefFilter::from(dial_info_filter).with_routing_domain(routing_domain),
|
||||
);
|
||||
if tighten {
|
||||
target_node_ref.set_sequencing(Sequencing::EnsureOrdered);
|
||||
}
|
||||
target_node_ref.set_sequencing(max_ordering.strict_sequencing());
|
||||
Some(NodeContactMethodKind::SignalReverse(
|
||||
relay_nr,
|
||||
target_node_ref,
|
||||
|
|
|
@ -15,14 +15,14 @@ pub async fn test_signed_node_info() {
|
|||
for ck in VALID_CRYPTO_KINDS {
|
||||
let vcrypto = crypto.get(ck).unwrap();
|
||||
let keypair = vcrypto.generate_keypair();
|
||||
let secret_key_group = SecretKeyGroup::from(SecretKey::new(ck, keypair.secret()));
|
||||
let secret_key_group = SecretKeyGroup::from(keypair.secret());
|
||||
|
||||
// Build test node info
|
||||
let node_info = NodeInfo::new(
|
||||
Timestamp::now(),
|
||||
VALID_ENVELOPE_VERSIONS.to_vec(),
|
||||
vec![CryptoInfo::VLD0 {
|
||||
public_key: keypair.key(),
|
||||
public_key: keypair.key().value(),
|
||||
}],
|
||||
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
|
||||
ProtocolTypeSet::all(),
|
||||
|
@ -82,7 +82,7 @@ pub async fn test_signed_node_info() {
|
|||
.expect_err("should not validate");
|
||||
|
||||
let invalid_crypto_kind = SignatureGroup::from(Signature::new(
|
||||
CryptoKind(*b"FOOO"),
|
||||
CryptoKind::new(*b"FOOO"),
|
||||
BareSignature::default(),
|
||||
));
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ impl DialInfoFilter {
|
|||
}
|
||||
pub fn is_ordered_only(&self) -> bool {
|
||||
for pt in self.protocol_type_set {
|
||||
if !pt.is_ordered() {
|
||||
if !matches!(pt.sequence_ordering(), SequenceOrdering::Ordered) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,12 @@ impl fmt::Display for Flow {
|
|||
|
||||
impl Flow {
|
||||
pub fn new(remote: PeerAddress, local: SocketAddress) -> Self {
|
||||
assert!(!remote.protocol_type().is_ordered() || !local.address().is_unspecified());
|
||||
assert!(
|
||||
!matches!(
|
||||
remote.protocol_type().sequence_ordering(),
|
||||
SequenceOrdering::Ordered
|
||||
) || !local.address().is_unspecified()
|
||||
);
|
||||
|
||||
Self {
|
||||
remote,
|
||||
|
|
|
@ -14,28 +14,12 @@ pub(crate) enum ProtocolType {
|
|||
}
|
||||
|
||||
impl ProtocolType {
|
||||
pub fn is_ordered(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ProtocolType::TCP | ProtocolType::WS | ProtocolType::WSS
|
||||
)
|
||||
}
|
||||
#[expect(dead_code)]
|
||||
pub fn minimum_sequencing(&self) -> Sequencing {
|
||||
pub fn sequence_ordering(&self) -> SequenceOrdering {
|
||||
match self {
|
||||
ProtocolType::UDP => Sequencing::NoPreference,
|
||||
ProtocolType::TCP => Sequencing::PreferOrdered,
|
||||
ProtocolType::WS => Sequencing::PreferOrdered,
|
||||
ProtocolType::WSS => Sequencing::PreferOrdered,
|
||||
}
|
||||
}
|
||||
#[expect(dead_code)]
|
||||
pub fn maximum_sequencing(&self) -> Sequencing {
|
||||
match self {
|
||||
ProtocolType::UDP => Sequencing::PreferOrdered,
|
||||
ProtocolType::TCP => Sequencing::EnsureOrdered,
|
||||
ProtocolType::WS => Sequencing::EnsureOrdered,
|
||||
ProtocolType::WSS => Sequencing::EnsureOrdered,
|
||||
ProtocolType::UDP => SequenceOrdering::Unordered,
|
||||
ProtocolType::TCP => SequenceOrdering::Ordered,
|
||||
ProtocolType::WS => SequenceOrdering::Ordered,
|
||||
ProtocolType::WSS => SequenceOrdering::Ordered,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,10 +27,10 @@ impl SignalInfo {
|
|||
receipt,
|
||||
peer_info: _,
|
||||
} => {
|
||||
if receipt.len() < MIN_RECEIPT_SIZE {
|
||||
if receipt.len() < RCP0_MIN_RECEIPT_SIZE {
|
||||
return Err(RPCError::protocol("SignalInfo HolePunch receipt too short"));
|
||||
}
|
||||
if receipt.len() > MAX_RECEIPT_SIZE {
|
||||
if receipt.len() > RCP0_MAX_RECEIPT_SIZE {
|
||||
return Err(RPCError::protocol("SignalInfo HolePunch receipt too long"));
|
||||
}
|
||||
Ok(())
|
||||
|
@ -39,12 +39,12 @@ impl SignalInfo {
|
|||
receipt,
|
||||
peer_info: _,
|
||||
} => {
|
||||
if receipt.len() < MIN_RECEIPT_SIZE {
|
||||
if receipt.len() < RCP0_MIN_RECEIPT_SIZE {
|
||||
return Err(RPCError::protocol(
|
||||
"SignalInfo ReverseConnect receipt too short",
|
||||
));
|
||||
}
|
||||
if receipt.len() > MAX_RECEIPT_SIZE {
|
||||
if receipt.len() > RCP0_MAX_RECEIPT_SIZE {
|
||||
return Err(RPCError::protocol(
|
||||
"SignalInfo ReverseConnect receipt too long",
|
||||
));
|
||||
|
|
|
@ -5,7 +5,7 @@ impl_veilid_log_facility!("rtab");
|
|||
|
||||
/// Routing Table Bucket
|
||||
/// Stores map of public keys to entries, which may be in multiple routing tables per crypto kind
|
||||
/// Keeps entries at a particular 'dht distance' from this cryptokind's node id
|
||||
/// Keeps entries at a particular 'hash coordinate distance' from this cryptokind's node id
|
||||
/// Helps to keep managed lists at particular distances so we can evict nodes by priority
|
||||
/// where the priority comes from liveness and age of the entry (older is better)
|
||||
pub struct Bucket {
|
||||
|
|
|
@ -162,10 +162,8 @@ impl fmt::Display for BucketEntryLocalNetwork {
|
|||
/// The data associated with each bucket entry
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct BucketEntryInner {
|
||||
/// The node ids matching this bucket entry, with the cryptography versions supported by this node as the 'kind' field
|
||||
validated_node_ids: NodeIdGroup,
|
||||
/// The node ids claimed by the remote node that use cryptography versions we do not support
|
||||
unsupported_node_ids: NodeIdGroup,
|
||||
/// The node ids matching this bucket entry
|
||||
node_ids: NodeIdGroup,
|
||||
/// The set of envelope versions supported by the node inclusive of the requirements of any relay the node may be using
|
||||
envelope_support: Vec<EnvelopeVersion>,
|
||||
/// If this node has updated it's SignedNodeInfo since our network
|
||||
|
@ -220,8 +218,7 @@ pub(crate) struct BucketEntryInner {
|
|||
|
||||
impl fmt::Display for BucketEntryInner {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "validated_node_ids: {}", self.validated_node_ids)?;
|
||||
writeln!(f, "unsupported_node_ids: {}", self.unsupported_node_ids)?;
|
||||
writeln!(f, "node_ids: {}", self.node_ids)?;
|
||||
writeln!(f, "envelope_support: {:?}", self.envelope_support)?;
|
||||
writeln!(
|
||||
f,
|
||||
|
@ -281,9 +278,46 @@ impl BucketEntryInner {
|
|||
|
||||
/// Get all node ids
|
||||
pub fn node_ids(&self) -> NodeIdGroup {
|
||||
let mut node_ids = self.validated_node_ids.clone();
|
||||
node_ids.add_all(&self.unsupported_node_ids);
|
||||
node_ids
|
||||
self.node_ids.clone()
|
||||
}
|
||||
|
||||
/// Get public keys
|
||||
pub fn public_keys(&self, routing_domain: RoutingDomain) -> PublicKeyGroup {
|
||||
match routing_domain {
|
||||
RoutingDomain::LocalNetwork => self
|
||||
.local_network
|
||||
.peer_info
|
||||
.as_ref()
|
||||
.map(|x| x.node_info().public_keys())
|
||||
.unwrap_or_default(),
|
||||
RoutingDomain::PublicInternet => self
|
||||
.public_internet
|
||||
.peer_info
|
||||
.as_ref()
|
||||
.map(|x| x.node_info().public_keys())
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get best node id
|
||||
pub fn best_node_id(&self) -> Option<NodeId> {
|
||||
self.node_ids.first().cloned()
|
||||
}
|
||||
|
||||
/// Get best public key
|
||||
pub fn best_public_key(&self, routing_domain: RoutingDomain) -> Option<PublicKey> {
|
||||
match routing_domain {
|
||||
RoutingDomain::LocalNetwork => self
|
||||
.local_network
|
||||
.peer_info
|
||||
.as_ref()
|
||||
.and_then(|x| x.node_info().public_keys().first().cloned()),
|
||||
RoutingDomain::PublicInternet => self
|
||||
.public_internet
|
||||
.peer_info
|
||||
.as_ref()
|
||||
.and_then(|x| x.node_info().public_keys().first().cloned()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a node id for a particular crypto kind.
|
||||
|
@ -291,28 +325,17 @@ impl BucketEntryInner {
|
|||
/// Returns Ok(None) if no previous existing node id was associated with that crypto kind, or one existed but nothing changed.
|
||||
/// Results Err() if this operation would add more crypto kinds than we support
|
||||
pub fn add_node_id(&mut self, node_id: NodeId) -> EyreResult<Option<NodeId>> {
|
||||
let total_node_id_count = self.validated_node_ids.len() + self.unsupported_node_ids.len();
|
||||
let node_ids = if VALID_CRYPTO_KINDS.contains(&node_id.kind()) {
|
||||
&mut self.validated_node_ids
|
||||
} else {
|
||||
&mut self.unsupported_node_ids
|
||||
};
|
||||
|
||||
if let Some(old_node_id) = node_ids.get(node_id.kind()) {
|
||||
if let Some(old_node_id) = self.node_ids.get(node_id.kind()) {
|
||||
// If this was already there we do nothing
|
||||
if old_node_id == node_id {
|
||||
return Ok(None);
|
||||
}
|
||||
// Won't change number of crypto kinds, but the node id changed
|
||||
node_ids.add(node_id);
|
||||
self.node_ids.add(node_id);
|
||||
|
||||
return Ok(Some(old_node_id));
|
||||
}
|
||||
// Check to ensure we aren't adding more crypto kinds than we support
|
||||
if total_node_id_count == MAX_CRYPTO_KINDS {
|
||||
bail!("too many crypto kinds for this node");
|
||||
}
|
||||
node_ids.add(node_id);
|
||||
self.node_ids.add(node_id);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
@ -321,17 +344,7 @@ impl BucketEntryInner {
|
|||
/// Returns Some(node) any previous existing node id associated with that crypto kind
|
||||
/// Returns None if no previous existing node id was associated with that crypto kind
|
||||
pub fn remove_node_id(&mut self, crypto_kind: CryptoKind) -> Option<NodeId> {
|
||||
let node_ids = if VALID_CRYPTO_KINDS.contains(&crypto_kind) {
|
||||
&mut self.validated_node_ids
|
||||
} else {
|
||||
&mut self.unsupported_node_ids
|
||||
};
|
||||
|
||||
node_ids.remove(crypto_kind)
|
||||
}
|
||||
|
||||
pub fn best_node_id(&self) -> Option<NodeId> {
|
||||
self.validated_node_ids.best()
|
||||
self.node_ids.remove(crypto_kind)
|
||||
}
|
||||
|
||||
pub fn relay_ids(&self, routing_domain: RoutingDomain) -> Vec<NodeIdGroup> {
|
||||
|
@ -354,11 +367,11 @@ impl BucketEntryInner {
|
|||
|
||||
/// Get crypto kinds
|
||||
pub fn crypto_kinds(&self) -> Vec<CryptoKind> {
|
||||
self.validated_node_ids.kinds()
|
||||
self.node_ids.kinds()
|
||||
}
|
||||
/// Compare sets of crypto kinds
|
||||
pub fn common_crypto_kinds(&self, other: &[CryptoKind]) -> Vec<CryptoKind> {
|
||||
common_crypto_kinds(&self.validated_node_ids.kinds(), other)
|
||||
common_crypto_kinds(&self.node_ids.kinds(), other)
|
||||
}
|
||||
|
||||
/// All-of capability check
|
||||
|
@ -699,7 +712,7 @@ impl BucketEntryInner {
|
|||
// Check if the connection is still considered live
|
||||
let alive =
|
||||
// Should we check the connection table?
|
||||
if v.0.protocol_type().is_ordered() {
|
||||
if matches!(v.0.protocol_type().sequence_ordering(), SequenceOrdering::Ordered) {
|
||||
// Look the connection up in the connection manager and see if it's still there
|
||||
if let Some(connection_manager) = &opt_connection_manager {
|
||||
connection_manager.get_connection(v.0).is_some()
|
||||
|
@ -1095,13 +1108,16 @@ impl BucketEntryInner {
|
|||
ts: Timestamp,
|
||||
bytes: ByteCount,
|
||||
expects_answer: bool,
|
||||
ordered: bool,
|
||||
ordering: SequenceOrdering,
|
||||
) {
|
||||
self.transfer_stats_accounting.add_up(bytes);
|
||||
if ordered {
|
||||
self.answer_stats_accounting_ordered.record_question(ts);
|
||||
} else {
|
||||
self.answer_stats_accounting_unordered.record_question(ts);
|
||||
match ordering {
|
||||
SequenceOrdering::Ordered => {
|
||||
self.answer_stats_accounting_ordered.record_question(ts);
|
||||
}
|
||||
SequenceOrdering::Unordered => {
|
||||
self.answer_stats_accounting_unordered.record_question(ts);
|
||||
}
|
||||
}
|
||||
self.peer_stats.rpc_stats.messages_sent += 1;
|
||||
self.peer_stats.rpc_stats.failed_to_send = 0;
|
||||
|
@ -1125,43 +1141,53 @@ impl BucketEntryInner {
|
|||
send_ts: Timestamp,
|
||||
recv_ts: Timestamp,
|
||||
bytes: ByteCount,
|
||||
ordered: bool,
|
||||
ordering: SequenceOrdering,
|
||||
) {
|
||||
self.transfer_stats_accounting.add_down(bytes);
|
||||
if ordered {
|
||||
self.answer_stats_accounting_ordered.record_answer(recv_ts);
|
||||
self.peer_stats.rpc_stats.recent_lost_answers_ordered = 0;
|
||||
} else {
|
||||
self.answer_stats_accounting_unordered
|
||||
.record_answer(recv_ts);
|
||||
self.peer_stats.rpc_stats.recent_lost_answers_unordered = 0;
|
||||
|
||||
match ordering {
|
||||
SequenceOrdering::Ordered => {
|
||||
self.answer_stats_accounting_ordered.record_answer(recv_ts);
|
||||
self.peer_stats.rpc_stats.recent_lost_answers_ordered = 0;
|
||||
}
|
||||
SequenceOrdering::Unordered => {
|
||||
self.answer_stats_accounting_unordered
|
||||
.record_answer(recv_ts);
|
||||
self.peer_stats.rpc_stats.recent_lost_answers_unordered = 0;
|
||||
}
|
||||
}
|
||||
|
||||
self.peer_stats.rpc_stats.messages_rcvd += 1;
|
||||
self.peer_stats.rpc_stats.questions_in_flight -= 1;
|
||||
self.record_latency(recv_ts.saturating_sub(send_ts));
|
||||
self.touch_last_seen(recv_ts);
|
||||
}
|
||||
pub(super) fn lost_answer(&mut self, ordered: bool) {
|
||||
pub(super) fn lost_answer(&mut self, ordering: SequenceOrdering) {
|
||||
let cur_ts = Timestamp::now();
|
||||
if ordered {
|
||||
self.answer_stats_accounting_ordered
|
||||
.record_lost_answer(cur_ts);
|
||||
self.peer_stats.rpc_stats.recent_lost_answers_ordered += 1;
|
||||
if self.peer_stats.rpc_stats.recent_lost_answers_ordered
|
||||
> UNRELIABLE_LOST_ANSWERS_ORDERED
|
||||
{
|
||||
self.peer_stats.rpc_stats.first_consecutive_seen_ts = None;
|
||||
|
||||
match ordering {
|
||||
SequenceOrdering::Ordered => {
|
||||
self.answer_stats_accounting_ordered
|
||||
.record_lost_answer(cur_ts);
|
||||
self.peer_stats.rpc_stats.recent_lost_answers_ordered += 1;
|
||||
if self.peer_stats.rpc_stats.recent_lost_answers_ordered
|
||||
> UNRELIABLE_LOST_ANSWERS_ORDERED
|
||||
{
|
||||
self.peer_stats.rpc_stats.first_consecutive_seen_ts = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.answer_stats_accounting_unordered
|
||||
.record_lost_answer(cur_ts);
|
||||
self.peer_stats.rpc_stats.recent_lost_answers_unordered += 1;
|
||||
if self.peer_stats.rpc_stats.recent_lost_answers_unordered
|
||||
> UNRELIABLE_LOST_ANSWERS_UNORDERED
|
||||
{
|
||||
self.peer_stats.rpc_stats.first_consecutive_seen_ts = None;
|
||||
SequenceOrdering::Unordered => {
|
||||
self.answer_stats_accounting_unordered
|
||||
.record_lost_answer(cur_ts);
|
||||
self.peer_stats.rpc_stats.recent_lost_answers_unordered += 1;
|
||||
if self.peer_stats.rpc_stats.recent_lost_answers_unordered
|
||||
> UNRELIABLE_LOST_ANSWERS_UNORDERED
|
||||
{
|
||||
self.peer_stats.rpc_stats.first_consecutive_seen_ts = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.peer_stats.rpc_stats.questions_in_flight -= 1;
|
||||
}
|
||||
pub(super) fn failed_to_send(&mut self, ts: Timestamp, expects_answer: bool) {
|
||||
|
@ -1199,8 +1225,7 @@ impl BucketEntry {
|
|||
|
||||
let now = Timestamp::now();
|
||||
let inner = BucketEntryInner {
|
||||
validated_node_ids: NodeIdGroup::from(first_node_id),
|
||||
unsupported_node_ids: NodeIdGroup::new(),
|
||||
node_ids: NodeIdGroup::from(first_node_id),
|
||||
envelope_support: Vec::new(),
|
||||
updated_since_last_network_change: false,
|
||||
last_flows: BTreeMap::new(),
|
||||
|
|
|
@ -339,7 +339,7 @@ impl RoutingTable {
|
|||
let is_relaying = node
|
||||
.operate(|_rti, e| {
|
||||
e.node_info(RoutingDomain::PublicInternet)
|
||||
.map(|ni| our_node_ids.contains_any(&ni.relay_ids()))
|
||||
.map(|ni| our_node_ids.contains_any_from_slice(&ni.relay_ids()))
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ impl RoutingTable {
|
|||
pub fn find_preferred_closest_peers(
|
||||
&self,
|
||||
routing_domain: RoutingDomain,
|
||||
hash_coordinate: &HashDigest,
|
||||
hash_coordinate: HashCoordinate,
|
||||
capabilities: &[VeilidCapability],
|
||||
) -> NetworkResult<Vec<Arc<PeerInfo>>> {
|
||||
if Crypto::validate_crypto_kind(hash_coordinate.kind()).is_err() {
|
||||
|
@ -71,7 +71,7 @@ impl RoutingTable {
|
|||
pub fn find_preferred_peers_closer_to_key(
|
||||
&self,
|
||||
routing_domain: RoutingDomain,
|
||||
hash_coordinate: &HashDigest,
|
||||
hash_coordinate: HashCoordinate,
|
||||
required_capabilities: Vec<VeilidCapability>,
|
||||
) -> NetworkResult<Vec<Arc<PeerInfo>>> {
|
||||
// add node information for the requesting node to our routing table
|
||||
|
@ -80,18 +80,9 @@ impl RoutingTable {
|
|||
|
||||
// find N nodes closest to the target node in our routing table
|
||||
// ensure the nodes returned are only the ones closer to the target node than ourself
|
||||
let crypto = self.crypto();
|
||||
let Some(vcrypto) = crypto.get(crypto_kind) else {
|
||||
return NetworkResult::invalid_message("unsupported cryptosystem");
|
||||
};
|
||||
let vcrypto = &vcrypto;
|
||||
let own_distance = own_node_id.to_hash_coordinate().distance(&hash_coordinate);
|
||||
|
||||
let own_distance = vcrypto.distance(
|
||||
&BareHashDigest::from(own_node_id.value()),
|
||||
&hash_coordinate.value(),
|
||||
);
|
||||
|
||||
let value = hash_coordinate.value();
|
||||
let hash_coordinate2 = hash_coordinate.clone();
|
||||
let filter = Box::new(
|
||||
move |_rti: &RoutingTableInner, opt_entry: Option<Arc<BucketEntry>>| {
|
||||
// Exclude our own node
|
||||
|
@ -112,8 +103,9 @@ impl RoutingTable {
|
|||
let Some(entry_node_id) = e.node_ids().get(crypto_kind) else {
|
||||
return false;
|
||||
};
|
||||
let entry_distance = vcrypto
|
||||
.distance(&BareHashDigest::from(entry_node_id.value()), &value.clone());
|
||||
let entry_distance = entry_node_id
|
||||
.to_hash_coordinate()
|
||||
.distance(&hash_coordinate2);
|
||||
if entry_distance >= own_distance {
|
||||
return false;
|
||||
}
|
||||
|
@ -151,10 +143,9 @@ impl RoutingTable {
|
|||
|
||||
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
||||
// This same test is used on the other side so we vet things here
|
||||
let valid = match Self::verify_peers_closer(
|
||||
vcrypto,
|
||||
&own_node_id.clone().into(),
|
||||
&hash_coordinate.clone(),
|
||||
let valid = match self.verify_peers_closer(
|
||||
own_node_id.to_hash_coordinate(),
|
||||
hash_coordinate.clone(),
|
||||
&closest_nodes,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
|
@ -175,24 +166,22 @@ impl RoutingTable {
|
|||
/// Determine if set of peers is closer to key_near than key_far is to key_near
|
||||
#[instrument(level = "trace", target = "rtab", skip_all, err)]
|
||||
pub fn verify_peers_closer(
|
||||
vcrypto: &crypto::CryptoSystemGuard<'_>,
|
||||
key_far: &HashDigest,
|
||||
key_near: &HashDigest,
|
||||
&self,
|
||||
hash_coordinate_far: HashCoordinate,
|
||||
hash_coordinate_near: HashCoordinate,
|
||||
peers: &[Arc<PeerInfo>],
|
||||
) -> EyreResult<bool> {
|
||||
let kind = vcrypto.kind();
|
||||
|
||||
if key_far.kind() != kind || key_near.kind() != kind {
|
||||
if hash_coordinate_far.kind() != hash_coordinate_near.kind() {
|
||||
bail!("keys all need the same cryptosystem");
|
||||
}
|
||||
|
||||
let mut closer = true;
|
||||
let d_far = vcrypto.distance(key_far.ref_value(), key_near.ref_value());
|
||||
let d_far = hash_coordinate_far.distance(&hash_coordinate_near);
|
||||
for peer in peers {
|
||||
let Some(key_peer) = peer.node_ids().get(kind) else {
|
||||
let Some(key_peer) = peer.node_ids().get(hash_coordinate_far.kind()) else {
|
||||
bail!("peers need to have a key with the same cryptosystem");
|
||||
};
|
||||
let d_near = vcrypto.distance(key_near.ref_value(), &key_peer.value().into());
|
||||
let d_near = hash_coordinate_near.distance(&key_peer.to_hash_coordinate());
|
||||
if d_far < d_near {
|
||||
let warning = format!(
|
||||
r#"peer: {}
|
||||
|
@ -202,14 +191,13 @@ far (self): {}
|
|||
d_far: {}
|
||||
cmp: {:?}"#,
|
||||
key_peer,
|
||||
key_near,
|
||||
key_far,
|
||||
hash_coordinate_near,
|
||||
hash_coordinate_far,
|
||||
d_near,
|
||||
d_far,
|
||||
d_near.cmp(&d_far)
|
||||
);
|
||||
let crypto = vcrypto.crypto();
|
||||
veilid_log!(crypto warn "{}", warning);
|
||||
veilid_log!(self warn "{}", warning);
|
||||
closer = false;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -36,9 +36,7 @@ impl_veilid_log_facility!("rtab");
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Routing table bucket count (one per bit per 32 byte node id)
|
||||
pub const BUCKET_COUNT: usize = 256;
|
||||
/// Fixed length for NodeId in bytes
|
||||
pub const NODE_ID_LENGTH: usize = 32;
|
||||
pub const BUCKET_COUNT: usize = HASH_COORDINATE_LENGTH * 8;
|
||||
|
||||
/// Minimum number of nodes we need, per crypto kind, per routing domain, or we trigger a bootstrap
|
||||
pub const MIN_BOOTSTRAP_CONNECTIVITY_PEERS: usize = 4;
|
||||
|
@ -375,18 +373,9 @@ impl RoutingTable {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn matches_own_node_id_key(&self, node_id_key: &BareNodeId) -> bool {
|
||||
for tk in self.node_ids().iter() {
|
||||
if tk.ref_value() == node_id_key {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Produce node id from public key
|
||||
pub fn generate_node_id(&self, public_key: &PublicKey) -> VeilidAPIResult<NodeId> {
|
||||
if public_key.ref_value().len() == NODE_ID_LENGTH {
|
||||
if public_key.ref_value().len() == HASH_COORDINATE_LENGTH {
|
||||
return Ok(NodeId::new(
|
||||
public_key.kind(),
|
||||
BareNodeId::new(public_key.ref_value()),
|
||||
|
@ -399,13 +388,13 @@ impl RoutingTable {
|
|||
|
||||
let idhash = vcrypto.generate_hash(public_key.ref_value());
|
||||
assert!(
|
||||
idhash.len() >= NODE_ID_LENGTH,
|
||||
idhash.ref_value().len() >= HASH_COORDINATE_LENGTH,
|
||||
"generate_hash needs to produce at least {} bytes",
|
||||
NODE_ID_LENGTH
|
||||
HASH_COORDINATE_LENGTH
|
||||
);
|
||||
Ok(NodeId::new(
|
||||
public_key.kind(),
|
||||
BareNodeId::new(&idhash[0..NODE_ID_LENGTH]),
|
||||
BareNodeId::new(&idhash.ref_value()[0..HASH_COORDINATE_LENGTH]),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -413,16 +402,12 @@ impl RoutingTable {
|
|||
if node_id.ref_value().len() * 8 != BUCKET_COUNT {
|
||||
bail!("NodeId should be hashed down to BUCKET_COUNT bits");
|
||||
}
|
||||
let crypto = self.crypto();
|
||||
let self_node_id_key = self.node_id(node_id.kind()).value();
|
||||
let vcrypto = crypto.get(node_id.kind()).unwrap();
|
||||
let self_hash_coordinate = self.node_id(node_id.kind()).to_hash_coordinate();
|
||||
Ok((
|
||||
node_id.kind(),
|
||||
vcrypto
|
||||
.distance(
|
||||
&BareHashDigest::from(node_id.value()),
|
||||
&BareHashDigest::from(self_node_id_key),
|
||||
)
|
||||
node_id
|
||||
.to_hash_coordinate()
|
||||
.distance(&self_hash_coordinate)
|
||||
.first_nonzero_bit()
|
||||
.unwrap(),
|
||||
))
|
||||
|
@ -615,6 +600,7 @@ impl RoutingTable {
|
|||
self.inner.read().dial_info_details(domain)
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn all_filtered_dial_info_details(
|
||||
&self,
|
||||
routing_domain_set: RoutingDomainSet,
|
||||
|
@ -676,6 +662,7 @@ impl RoutingTable {
|
|||
}
|
||||
|
||||
/// Return a list of the current valid bootstrap peers in a particular routing domain
|
||||
#[expect(dead_code)]
|
||||
pub fn get_bootstrap_peers(&self, routing_domain: RoutingDomain) -> Vec<NodeRef> {
|
||||
self.inner.read().get_bootstrap_peers(routing_domain)
|
||||
}
|
||||
|
@ -932,26 +919,30 @@ impl RoutingTable {
|
|||
pub fn find_preferred_closest_nodes<'a, T, O>(
|
||||
&self,
|
||||
node_count: usize,
|
||||
node_id: HashDigest,
|
||||
hash_coordinate: HashCoordinate,
|
||||
filters: VecDeque<RoutingTableEntryFilter>,
|
||||
transform: T,
|
||||
) -> VeilidAPIResult<Vec<O>>
|
||||
where
|
||||
T: for<'r> FnMut(&'r RoutingTableInner, Option<Arc<BucketEntry>>) -> O + Send,
|
||||
{
|
||||
self.inner
|
||||
.read()
|
||||
.find_preferred_closest_nodes(node_count, node_id, filters, transform)
|
||||
self.inner.read().find_preferred_closest_nodes(
|
||||
node_count,
|
||||
hash_coordinate,
|
||||
filters,
|
||||
transform,
|
||||
)
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn sort_and_clean_closest_noderefs(
|
||||
&self,
|
||||
node_id: HashDigest,
|
||||
hash_coordinate: HashCoordinate,
|
||||
closest_nodes: &[NodeRef],
|
||||
) -> Vec<NodeRef> {
|
||||
self.inner
|
||||
.read()
|
||||
.sort_and_clean_closest_noderefs(node_id, closest_nodes)
|
||||
.sort_and_clean_closest_noderefs(hash_coordinate, closest_nodes)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, peer_info_list))]
|
||||
|
@ -1076,6 +1067,7 @@ impl RoutingTable {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, filter, metric), ret)]
|
||||
#[expect(dead_code)]
|
||||
pub fn find_fastest_node(
|
||||
&self,
|
||||
cur_ts: Timestamp,
|
||||
|
@ -1099,6 +1091,7 @@ impl RoutingTable {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self, filter, metric), ret)]
|
||||
#[expect(dead_code)]
|
||||
pub fn get_node_speed_percentile(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
|
|
|
@ -39,7 +39,6 @@ impl<'a, N: NodeRefAccessorsTrait + NodeRefOperateTrait + fmt::Debug + fmt::Disp
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn unlocked(&self) -> N {
|
||||
self.nr.clone()
|
||||
}
|
||||
|
|
|
@ -50,9 +50,15 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait
|
|||
fn node_ids(&self) -> NodeIdGroup {
|
||||
self.operate(|_rti, e| e.node_ids())
|
||||
}
|
||||
fn public_keys(&self, routing_domain: RoutingDomain) -> PublicKeyGroup {
|
||||
self.operate(|_rti, e| e.public_keys(routing_domain))
|
||||
}
|
||||
fn best_node_id(&self) -> Option<NodeId> {
|
||||
self.operate(|_rti, e| e.best_node_id())
|
||||
}
|
||||
fn best_public_key(&self, routing_domain: RoutingDomain) -> Option<PublicKey> {
|
||||
self.operate(|_rti, e| e.best_public_key(routing_domain))
|
||||
}
|
||||
|
||||
fn relay_ids(&self, routing_domain: RoutingDomain) -> Vec<NodeIdGroup> {
|
||||
self.operate(|_rti, e| e.relay_ids(routing_domain))
|
||||
|
@ -254,7 +260,7 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait
|
|||
return false;
|
||||
};
|
||||
let our_node_ids = rti.routing_table().node_ids();
|
||||
our_node_ids.contains_any(relay_ids.as_slice())
|
||||
our_node_ids.contains_any_from_slice(relay_ids.as_slice())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -284,11 +290,11 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait
|
|||
ts: Timestamp,
|
||||
bytes: ByteCount,
|
||||
expects_answer: bool,
|
||||
ordered: bool,
|
||||
ordering: SequenceOrdering,
|
||||
) {
|
||||
self.operate_mut(|rti, e| {
|
||||
rti.transfer_stats_accounting().add_up(bytes);
|
||||
e.question_sent(ts, bytes, expects_answer, ordered);
|
||||
e.question_sent(ts, bytes, expects_answer, ordering);
|
||||
})
|
||||
}
|
||||
fn stats_question_rcvd(&self, ts: Timestamp, bytes: ByteCount) {
|
||||
|
@ -308,18 +314,18 @@ pub(crate) trait NodeRefCommonTrait: NodeRefAccessorsTrait + NodeRefOperateTrait
|
|||
send_ts: Timestamp,
|
||||
recv_ts: Timestamp,
|
||||
bytes: ByteCount,
|
||||
ordered: bool,
|
||||
ordering: SequenceOrdering,
|
||||
) {
|
||||
self.operate_mut(|rti, e| {
|
||||
rti.transfer_stats_accounting().add_down(bytes);
|
||||
rti.latency_stats_accounting()
|
||||
.record_latency(recv_ts.saturating_sub(send_ts));
|
||||
e.answer_rcvd(send_ts, recv_ts, bytes, ordered);
|
||||
e.answer_rcvd(send_ts, recv_ts, bytes, ordering);
|
||||
})
|
||||
}
|
||||
fn stats_lost_answer(&self, ordered: bool) {
|
||||
fn stats_lost_answer(&self, ordering: SequenceOrdering) {
|
||||
self.operate_mut(|_rti, e| {
|
||||
e.lost_answer(ordered);
|
||||
e.lost_answer(ordering);
|
||||
})
|
||||
}
|
||||
fn stats_failed_to_send(&self, ts: Timestamp, expects_answer: bool) {
|
||||
|
|
|
@ -8,7 +8,7 @@ impl_veilid_log_facility!("rtab");
|
|||
#[derive(Clone)]
|
||||
pub(crate) struct RouteHopData {
|
||||
/// The nonce used in the encryption ENC(Xn,DH(PKn,SKapr))
|
||||
pub nonce: BareNonce,
|
||||
pub nonce: Nonce,
|
||||
/// The encrypted blob
|
||||
pub blob: Vec<u8>,
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ impl RouteNode {
|
|||
RouteNode::NodeId(id) => {
|
||||
format!("{}", id)
|
||||
}
|
||||
RouteNode::PeerInfo(pi) => match pi.node_ids().best() {
|
||||
RouteNode::PeerInfo(pi) => match pi.node_ids().first() {
|
||||
Some(id) => format!("{}", id),
|
||||
None => {
|
||||
format!("?({})", pi.node_ids())
|
||||
|
|
|
@ -272,7 +272,7 @@ impl RouteSpecStore {
|
|||
}
|
||||
|
||||
// Exclude nodes we have specifically chosen to avoid
|
||||
if e.node_ids().contains_any(avoid_nodes) {
|
||||
if e.node_ids().contains_any_from_slice(avoid_nodes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -375,11 +375,11 @@ impl RouteSpecStore {
|
|||
// Relay check
|
||||
for their_relay_info in their_ni.relay_info_list() {
|
||||
// Exclude nodes whose relays we have chosen to avoid
|
||||
if their_relay_info.node_ids().contains_any(avoid_nodes) {
|
||||
if their_relay_info.node_ids().contains_any_from_slice(avoid_nodes) {
|
||||
return false;
|
||||
}
|
||||
// Exclude nodes whose relay is our own relay if we have one
|
||||
if their_relay_info.node_ids().contains_any(&own_relay_ids) {
|
||||
if their_relay_info.node_ids().contains_any_from_slice(&own_relay_ids) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -535,7 +535,7 @@ impl RouteSpecStore {
|
|||
}
|
||||
|
||||
// Ensure this route is viable by checking that each node can contact the next one
|
||||
let mut can_do_sequenced = true;
|
||||
let mut orderings = SequenceOrderingSet::all();
|
||||
if directions.contains(Direction::Outbound) {
|
||||
let mut previous_node = published_peer_info.clone();
|
||||
let mut reachable = true;
|
||||
|
@ -554,18 +554,18 @@ impl RouteSpecStore {
|
|||
break;
|
||||
}
|
||||
|
||||
// Check if we can do sequenced specifically
|
||||
if can_do_sequenced {
|
||||
// Check if we can do each ordering strictly
|
||||
for ordering in orderings {
|
||||
let cm = rti.get_contact_method(
|
||||
RoutingDomain::PublicInternet,
|
||||
previous_node.clone(),
|
||||
current_node.clone(),
|
||||
DialInfoFilter::all(),
|
||||
Sequencing::EnsureOrdered,
|
||||
ordering.strict_sequencing(),
|
||||
None,
|
||||
);
|
||||
if matches!(cm, ContactMethod::Unreachable) {
|
||||
can_do_sequenced = false;
|
||||
orderings.remove(ordering);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,18 +593,18 @@ impl RouteSpecStore {
|
|||
break;
|
||||
}
|
||||
|
||||
// Check if we can do sequenced specifically
|
||||
if can_do_sequenced {
|
||||
// Check if we can do each ordering strictly
|
||||
for ordering in orderings {
|
||||
let cm = rti.get_contact_method(
|
||||
RoutingDomain::PublicInternet,
|
||||
next_node.clone(),
|
||||
current_node.clone(),
|
||||
DialInfoFilter::all(),
|
||||
Sequencing::EnsureOrdered,
|
||||
ordering.strict_sequencing(),
|
||||
None,
|
||||
);
|
||||
if matches!(cm, ContactMethod::Unreachable) {
|
||||
can_do_sequenced = false;
|
||||
orderings.remove(ordering);
|
||||
}
|
||||
}
|
||||
next_node = current_node;
|
||||
|
@ -615,19 +615,19 @@ impl RouteSpecStore {
|
|||
}
|
||||
// Keep this route
|
||||
let route_nodes = permutation.to_vec();
|
||||
Some((route_nodes, can_do_sequenced))
|
||||
Some((route_nodes, orderings))
|
||||
}) as PermFunc;
|
||||
|
||||
let mut route_nodes: Vec<usize> = Vec::new();
|
||||
let mut can_do_sequenced: bool = true;
|
||||
let mut orderings = SequenceOrderingSet::new();
|
||||
|
||||
for start in 0..(nodes.len() - safety_spec.hop_count) {
|
||||
// Try the permutations available starting with 'start'
|
||||
if let Some((rn, cds)) =
|
||||
if let Some((rn, ord)) =
|
||||
with_route_permutations(safety_spec.hop_count, start, &mut perm_func)
|
||||
{
|
||||
route_nodes = rn;
|
||||
can_do_sequenced = cds;
|
||||
orderings = ord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -639,7 +639,7 @@ impl RouteSpecStore {
|
|||
|
||||
// Got a unique route, lets build the details, register it, and return it
|
||||
let hop_node_refs: Vec<NodeRef> = route_nodes.iter().map(|k| nodes[*k].clone()).collect();
|
||||
let mut route_set = BTreeMap::<BarePublicKey, RouteSpecDetail>::new();
|
||||
let mut route_set = BTreeMap::<PublicKey, RouteSpecDetail>::new();
|
||||
let crypto = self.crypto();
|
||||
for crypto_kind in crypto_kinds.iter().copied() {
|
||||
let vcrypto = crypto.get(crypto_kind).unwrap();
|
||||
|
@ -652,7 +652,6 @@ impl RouteSpecStore {
|
|||
route_set.insert(
|
||||
keypair.key(),
|
||||
RouteSpecDetail {
|
||||
crypto_kind,
|
||||
secret_key: keypair.secret(),
|
||||
hops,
|
||||
},
|
||||
|
@ -665,7 +664,7 @@ impl RouteSpecStore {
|
|||
hop_node_refs,
|
||||
directions,
|
||||
safety_spec.stability,
|
||||
can_do_sequenced,
|
||||
orderings,
|
||||
automatic,
|
||||
);
|
||||
|
||||
|
@ -697,12 +696,8 @@ impl RouteSpecStore {
|
|||
{
|
||||
let inner = &*self.inner.lock();
|
||||
let crypto = self.crypto();
|
||||
let Some(vcrypto) = crypto.get(public_key.kind()) else {
|
||||
veilid_log!(self debug "can't handle route with public key: {:?}", public_key);
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(rsid) = inner.content.get_id_by_key(public_key.ref_value()) else {
|
||||
let Some(rsid) = inner.content.get_id_by_key(public_key) else {
|
||||
veilid_log!(self debug target: "network_result", "route id does not exist: {:?}", public_key.ref_value());
|
||||
return None;
|
||||
};
|
||||
|
@ -710,7 +705,7 @@ impl RouteSpecStore {
|
|||
veilid_log!(self debug "route detail does not exist: {:?}", rsid);
|
||||
return None;
|
||||
};
|
||||
let Some(rsd) = rssd.get_route_by_key(public_key.ref_value()) else {
|
||||
let Some(rsd) = rssd.get_route_by_key(public_key) else {
|
||||
veilid_log!(self debug "route set {:?} does not have key: {:?}", rsid, public_key.ref_value());
|
||||
return None;
|
||||
};
|
||||
|
@ -723,28 +718,35 @@ impl RouteSpecStore {
|
|||
}
|
||||
// Validate signatures to ensure the route was handled by the nodes and not messed with
|
||||
// This is in private route (reverse) order as we are receiving over the route
|
||||
for (hop_n, hop_node_id) in rsd.hops.iter().rev().enumerate() {
|
||||
for (hop_n, hop_node_ref) in rssd.hop_node_refs().iter().rev().enumerate() {
|
||||
// The last hop is not signed, as the whole packet is signed
|
||||
if hop_n == signatures.len() {
|
||||
// Verify the node we received the routed operation from is the last hop in our route
|
||||
if hop_node_id != last_hop_id {
|
||||
veilid_log!(self debug "received routed operation from the wrong hop ({} should be {}) on private route {}", hop_node_id, last_hop_id, public_key);
|
||||
if !hop_node_ref.node_ids().contains(last_hop_id) {
|
||||
veilid_log!(self debug "received routed operation from the wrong hop ({} should be {}) on private route {}", hop_node_ref, last_hop_id, public_key);
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
let Some(hop_public_key) = hop_node_ref
|
||||
.public_keys(RoutingDomain::PublicInternet)
|
||||
.get(signatures[hop_n].kind())
|
||||
else {
|
||||
veilid_log!(self debug "no hop public key matching signature kind {} at hop {} for routed operation on private route {}", signatures[hop_n].kind(), hop_n, public_key);
|
||||
return None;
|
||||
};
|
||||
// Verify a signature for a hop node along the route
|
||||
match vcrypto.verify(
|
||||
&hop_node_id.ref_value().clone().into(),
|
||||
data,
|
||||
signatures[hop_n].ref_value(),
|
||||
) {
|
||||
let Some(vcrypto) = crypto.get(hop_public_key.kind()) else {
|
||||
veilid_log!(self debug "can't handle route hop with public key: {:?}", hop_public_key.kind());
|
||||
return None;
|
||||
};
|
||||
match vcrypto.verify(&hop_public_key, data, &signatures[hop_n]) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
veilid_log!(self debug "invalid signature for hop {} at {} on private route {}", hop_n, hop_node_id, public_key);
|
||||
veilid_log!(self debug "invalid signature for hop {} at {} on private route {}", hop_n, hop_node_ref, public_key);
|
||||
return None;
|
||||
}
|
||||
Err(e) => {
|
||||
veilid_log!(self debug "error verifying signature for hop {} at {} on private route {}: {}", hop_n, hop_node_id, public_key, e);
|
||||
veilid_log!(self debug "error verifying signature for hop {} at {} on private route {}: {}", hop_n, hop_node_ref, public_key, e);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -773,7 +775,7 @@ impl RouteSpecStore {
|
|||
};
|
||||
// Get the hops so we can match the route's hop length for safety
|
||||
// route length as well as marking nodes as unreliable if this fails
|
||||
let hops = rssd.hops_node_refs();
|
||||
let hops = rssd.hop_node_refs();
|
||||
(key, hops)
|
||||
};
|
||||
|
||||
|
@ -952,7 +954,10 @@ impl RouteSpecStore {
|
|||
&& rssd.hop_count() >= min_hop_count
|
||||
&& rssd.hop_count() <= max_hop_count
|
||||
&& rssd.get_directions().is_superset(directions)
|
||||
&& rssd.get_route_set_keys().kinds().contains(&crypto_kind)
|
||||
&& rssd
|
||||
.get_route_set_keys()
|
||||
.iter()
|
||||
.any(|x| x.kind() == crypto_kind)
|
||||
&& !rssd.is_published()
|
||||
&& !rssd.contains_nodes(avoid_nodes)
|
||||
{
|
||||
|
@ -1072,7 +1077,7 @@ impl RouteSpecStore {
|
|||
let Some(vcrypto) = crypto.get(crypto_kind) else {
|
||||
apibail_generic!("crypto not supported for route");
|
||||
};
|
||||
let pr_pubkey = private_route.public_key.value();
|
||||
let pr_pubkey = private_route.public_key.clone();
|
||||
|
||||
// See if we are using a safety route, if not, short circuit this operation
|
||||
let safety_spec = match safety_selection {
|
||||
|
@ -1114,7 +1119,7 @@ impl RouteSpecStore {
|
|||
routing_table.public_key(crypto_kind),
|
||||
private_route,
|
||||
),
|
||||
secret: routing_table.secret_key(crypto_kind).value(),
|
||||
secret: routing_table.secret_key(crypto_kind),
|
||||
first_hop,
|
||||
});
|
||||
}
|
||||
|
@ -1209,12 +1214,42 @@ impl RouteSpecStore {
|
|||
// Each loop mutates 'nonce', and 'blob_data'
|
||||
let mut nonce = vcrypto.random_nonce();
|
||||
// Forward order (safety route), but inside-out
|
||||
for h in (1..safety_rsd.hops.len()).rev() {
|
||||
for h in (1..safety_rssd.hop_node_refs().len()).rev() {
|
||||
let hop_node_ref = safety_rssd.hop_node_ref(h).unwrap();
|
||||
let Some(hop_node_id) = hop_node_ref.locked(rti).node_ids().get(crypto_kind) else {
|
||||
apibail_invalid_argument!(
|
||||
"no hop node id for route hop",
|
||||
"crypto_kind",
|
||||
crypto_kind
|
||||
);
|
||||
};
|
||||
let Some(hop_public_key) = hop_node_ref
|
||||
.locked(rti)
|
||||
.public_keys(RoutingDomain::PublicInternet)
|
||||
.get(crypto_kind)
|
||||
else {
|
||||
apibail_invalid_argument!(
|
||||
"no hop public key for route hop",
|
||||
"crypto_kind",
|
||||
crypto_kind
|
||||
);
|
||||
};
|
||||
let Some(hop_peer_info) = hop_node_ref
|
||||
.locked(rti)
|
||||
.get_peer_info(RoutingDomain::PublicInternet)
|
||||
else {
|
||||
apibail_invalid_argument!(
|
||||
"no hop peer info for route hop",
|
||||
"crypto_kind",
|
||||
crypto_kind
|
||||
);
|
||||
};
|
||||
|
||||
// Get blob to encrypt for next hop
|
||||
blob_data = {
|
||||
// Encrypt the previous blob ENC(nonce, DH(PKhop,SKsr))
|
||||
let dh_secret = vcrypto
|
||||
.cached_dh(&safety_rsd.hops[h].value().into(), &safety_rsd.secret_key)
|
||||
.cached_dh(&hop_public_key, &safety_rsd.secret_key)
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
let enc_msg_data = vcrypto
|
||||
.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)
|
||||
|
@ -1230,21 +1265,10 @@ impl RouteSpecStore {
|
|||
let route_hop = RouteHop {
|
||||
node: if optimize {
|
||||
// Optimized, no peer info, just the dht key
|
||||
RouteNode::NodeId(safety_rsd.hops[h].clone())
|
||||
RouteNode::NodeId(hop_node_id)
|
||||
} else {
|
||||
// Full peer info, required until we are sure the route has been fully established
|
||||
let node_id = safety_rsd.hops[h].clone();
|
||||
let pi = rti
|
||||
.with_node_entry(node_id, |entry| {
|
||||
entry.with(rti, |_rti, e| {
|
||||
e.get_peer_info(RoutingDomain::PublicInternet)
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
if pi.is_none() {
|
||||
apibail_internal!("peer info should exist for route but doesn't");
|
||||
}
|
||||
RouteNode::PeerInfo(pi.unwrap())
|
||||
RouteNode::PeerInfo(hop_peer_info)
|
||||
},
|
||||
next_hop: Some(route_hop_data),
|
||||
};
|
||||
|
@ -1265,8 +1289,21 @@ impl RouteSpecStore {
|
|||
}
|
||||
|
||||
// Encode first RouteHopData
|
||||
let hop_node_ref = safety_rssd.hop_node_ref(0).unwrap();
|
||||
let Some(hop_public_key) = hop_node_ref
|
||||
.locked(rti)
|
||||
.public_keys(RoutingDomain::PublicInternet)
|
||||
.get(crypto_kind)
|
||||
else {
|
||||
apibail_invalid_argument!(
|
||||
"no hop public key for route hop",
|
||||
"crypto_kind",
|
||||
crypto_kind
|
||||
);
|
||||
};
|
||||
|
||||
let dh_secret = vcrypto
|
||||
.cached_dh(&safety_rsd.hops[0].value().into(), &safety_rsd.secret_key)
|
||||
.cached_dh(&hop_public_key, &safety_rsd.secret_key)
|
||||
.map_err(VeilidAPIError::internal)?;
|
||||
let enc_msg_data = vcrypto
|
||||
.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)
|
||||
|
@ -1282,7 +1319,7 @@ impl RouteSpecStore {
|
|||
|
||||
// Build safety route
|
||||
let safety_route = SafetyRoute {
|
||||
public_key: PublicKey::new(crypto_kind, sr_pubkey.clone()),
|
||||
public_key: sr_pubkey,
|
||||
hops,
|
||||
};
|
||||
|
||||
|
@ -1315,7 +1352,7 @@ impl RouteSpecStore {
|
|||
safety_spec: &SafetySpec,
|
||||
direction: DirectionSet,
|
||||
avoid_nodes: &[NodeId],
|
||||
) -> VeilidAPIResult<BarePublicKey> {
|
||||
) -> VeilidAPIResult<PublicKey> {
|
||||
// Ensure the total hop count isn't too long for our config
|
||||
let max_route_hop_count = self.max_route_hop_count;
|
||||
if safety_spec.hop_count == 0 {
|
||||
|
@ -1340,7 +1377,7 @@ impl RouteSpecStore {
|
|||
if let Some(preferred_key) = preferred_rssd.get_route_set_keys().get(crypto_kind) {
|
||||
// Only use the preferred route if it doesn't contain the avoid nodes
|
||||
if !preferred_rssd.contains_nodes(avoid_nodes) {
|
||||
return Ok(preferred_key.value());
|
||||
return Ok(preferred_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1378,8 +1415,7 @@ impl RouteSpecStore {
|
|||
.unwrap()
|
||||
.get_route_set_keys()
|
||||
.get(crypto_kind)
|
||||
.unwrap()
|
||||
.value();
|
||||
.unwrap();
|
||||
|
||||
Ok(sr_pubkey)
|
||||
}
|
||||
|
@ -1391,7 +1427,7 @@ impl RouteSpecStore {
|
|||
crypto_kind: CryptoKind,
|
||||
safety_spec: &SafetySpec,
|
||||
avoid_nodes: &[NodeId],
|
||||
) -> VeilidAPIResult<BarePublicKey> {
|
||||
) -> VeilidAPIResult<PublicKey> {
|
||||
let inner = &mut *self.inner.lock();
|
||||
let routing_table = self.routing_table();
|
||||
let rti = &mut *routing_table.inner.write();
|
||||
|
@ -1408,8 +1444,9 @@ impl RouteSpecStore {
|
|||
|
||||
fn assemble_private_route_inner(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
key: &PublicKey,
|
||||
rsd: &RouteSpecDetail,
|
||||
rssd: &RouteSetSpecDetail,
|
||||
optimized: bool,
|
||||
) -> VeilidAPIResult<PrivateRoute> {
|
||||
let routing_table = self.routing_table();
|
||||
|
@ -1417,12 +1454,9 @@ impl RouteSpecStore {
|
|||
|
||||
// Ensure we get the crypto for it
|
||||
let crypto = routing_table.network_manager().crypto();
|
||||
let Some(vcrypto) = crypto.get(rsd.crypto_kind) else {
|
||||
apibail_invalid_argument!(
|
||||
"crypto not supported for route",
|
||||
"rsd.crypto_kind",
|
||||
rsd.crypto_kind
|
||||
);
|
||||
let crypto_kind = key.kind();
|
||||
let Some(vcrypto) = crypto.get(crypto_kind) else {
|
||||
apibail_invalid_argument!("crypto not supported for route", "crypto_kind", crypto_kind);
|
||||
};
|
||||
|
||||
// Ensure our network class is valid before attempting to assemble any routes
|
||||
|
@ -1434,11 +1468,11 @@ impl RouteSpecStore {
|
|||
// Make innermost route hop to our own node
|
||||
let mut route_hop = RouteHop {
|
||||
node: if optimized {
|
||||
let Some(node_id) = routing_table.node_ids().get(rsd.crypto_kind) else {
|
||||
let Some(node_id) = routing_table.node_ids().get(crypto_kind) else {
|
||||
apibail_invalid_argument!(
|
||||
"missing node id for crypto kind",
|
||||
"rsd.crypto_kind",
|
||||
rsd.crypto_kind
|
||||
"crypto_kind",
|
||||
crypto_kind
|
||||
);
|
||||
};
|
||||
RouteNode::NodeId(node_id)
|
||||
|
@ -1448,10 +1482,37 @@ impl RouteSpecStore {
|
|||
next_hop: None,
|
||||
};
|
||||
|
||||
// Loop for each hop
|
||||
let hop_count = rsd.hops.len();
|
||||
// iterate hops in private route order (reverse, but inside out)
|
||||
for h in 0..hop_count {
|
||||
// Iterate hops in private route order (reverse, but inside out)
|
||||
for hop_node_ref in rssd.hop_node_refs() {
|
||||
let hop_node_ref = hop_node_ref.locked(rti);
|
||||
|
||||
let Some(hop_node_id) = hop_node_ref.node_ids().get(crypto_kind) else {
|
||||
apibail_invalid_argument!(
|
||||
"no hop node id for route hop",
|
||||
"crypto_kind",
|
||||
crypto_kind
|
||||
);
|
||||
};
|
||||
let Some(hop_public_key) = hop_node_ref
|
||||
.public_keys(RoutingDomain::PublicInternet)
|
||||
.get(crypto_kind)
|
||||
else {
|
||||
apibail_invalid_argument!(
|
||||
"no hop public key for route hop",
|
||||
"crypto_kind",
|
||||
crypto_kind
|
||||
);
|
||||
};
|
||||
let Some(hop_peer_info) = hop_node_ref.get_peer_info(RoutingDomain::PublicInternet)
|
||||
else {
|
||||
apibail_invalid_argument!(
|
||||
"no hop peer info for route hop",
|
||||
"crypto_kind",
|
||||
crypto_kind
|
||||
);
|
||||
};
|
||||
|
||||
// Encrypt the previous blob ENC(nonce, DH(PKhop,SKpr))
|
||||
let nonce = vcrypto.random_nonce();
|
||||
|
||||
let blob_data = {
|
||||
|
@ -1461,8 +1522,7 @@ impl RouteSpecStore {
|
|||
message_builder_to_vec(rh_message)?
|
||||
};
|
||||
|
||||
// Encrypt the previous blob ENC(nonce, DH(PKhop,SKpr))
|
||||
let dh_secret = vcrypto.cached_dh(&rsd.hops[h].value().into(), &rsd.secret_key)?;
|
||||
let dh_secret = vcrypto.cached_dh(&hop_public_key, &rsd.secret_key)?;
|
||||
let enc_msg_data =
|
||||
vcrypto.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)?;
|
||||
let route_hop_data = RouteHopData {
|
||||
|
@ -1473,28 +1533,17 @@ impl RouteSpecStore {
|
|||
route_hop = RouteHop {
|
||||
node: if optimized {
|
||||
// Optimized, no peer info, just the dht key
|
||||
RouteNode::NodeId(rsd.hops[h].clone())
|
||||
RouteNode::NodeId(hop_node_id)
|
||||
} else {
|
||||
// Full peer info, required until we are sure the route has been fully established
|
||||
let node_id = rsd.hops[h].clone();
|
||||
let pi = rti
|
||||
.with_node_entry(node_id, |entry| {
|
||||
entry.with(rti, |_rti, e| {
|
||||
e.get_peer_info(RoutingDomain::PublicInternet)
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
if pi.is_none() {
|
||||
apibail_internal!("peer info should exist for route but doesn't");
|
||||
}
|
||||
RouteNode::PeerInfo(pi.unwrap())
|
||||
RouteNode::PeerInfo(hop_peer_info)
|
||||
},
|
||||
next_hop: Some(route_hop_data),
|
||||
}
|
||||
}
|
||||
|
||||
let private_route = PrivateRoute {
|
||||
public_key: PublicKey::new(rsd.crypto_kind, key.clone()),
|
||||
public_key: key.clone(),
|
||||
hops: PrivateRouteHops::FirstHop(Box::new(route_hop)),
|
||||
};
|
||||
Ok(private_route)
|
||||
|
@ -1505,7 +1554,7 @@ impl RouteSpecStore {
|
|||
#[instrument(level = "trace", target = "route", skip_all)]
|
||||
pub fn assemble_private_route(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
key: &PublicKey,
|
||||
optimized: Option<bool>,
|
||||
) -> VeilidAPIResult<PrivateRoute> {
|
||||
let inner: &RouteSpecStoreInner = &self.inner.lock();
|
||||
|
@ -1525,7 +1574,7 @@ impl RouteSpecStore {
|
|||
.get_route_by_key(key)
|
||||
.expect("route key index is broken");
|
||||
|
||||
self.assemble_private_route_inner(key, rsd, optimized)
|
||||
self.assemble_private_route_inner(key, rsd, rssd, optimized)
|
||||
}
|
||||
|
||||
/// Assemble private route set for publication
|
||||
|
@ -1547,7 +1596,7 @@ impl RouteSpecStore {
|
|||
|
||||
let mut out = Vec::new();
|
||||
for (key, rsd) in rssd.iter_route_set() {
|
||||
out.push(self.assemble_private_route_inner(key, rsd, optimized)?);
|
||||
out.push(self.assemble_private_route_inner(key, rsd, rssd, optimized)?);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
@ -1631,7 +1680,7 @@ impl RouteSpecStore {
|
|||
}
|
||||
|
||||
/// Get a route id for a route's public key
|
||||
pub fn get_route_id_for_key(&self, key: &BarePublicKey) -> Option<RouteId> {
|
||||
pub fn get_route_id_for_key(&self, key: &PublicKey) -> Option<RouteId> {
|
||||
let inner = &mut *self.inner.lock();
|
||||
// Check for local route
|
||||
if let Some(id) = inner.content.get_id_by_key(key) {
|
||||
|
@ -1650,7 +1699,7 @@ impl RouteSpecStore {
|
|||
/// This happens when you communicate with a private route without a safety route
|
||||
pub fn has_remote_private_route_seen_our_node_info(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
key: &PublicKey,
|
||||
published_peer_info: &PeerInfo,
|
||||
) -> bool {
|
||||
let inner = &*self.inner.lock();
|
||||
|
@ -1680,7 +1729,7 @@ impl RouteSpecStore {
|
|||
/// was that node that had the private route.
|
||||
pub fn mark_remote_private_route_seen_our_node_info(
|
||||
&self,
|
||||
key: &BarePublicKey,
|
||||
key: &PublicKey,
|
||||
cur_ts: Timestamp,
|
||||
) -> VeilidAPIResult<()> {
|
||||
let Some(our_node_info_ts) = self
|
||||
|
@ -1711,22 +1760,14 @@ impl RouteSpecStore {
|
|||
}
|
||||
|
||||
/// Get the route statistics for any route we know about, local or remote
|
||||
pub fn with_route_stats_mut<F, R>(
|
||||
&self,
|
||||
cur_ts: Timestamp,
|
||||
key: &BarePublicKey,
|
||||
f: F,
|
||||
) -> Option<R>
|
||||
pub fn with_route_stats_mut<F, R>(&self, cur_ts: Timestamp, key: &PublicKey, f: F) -> Option<R>
|
||||
where
|
||||
F: FnOnce(&mut RouteStats) -> R,
|
||||
{
|
||||
let inner = &mut *self.inner.lock();
|
||||
|
||||
// Check for stub route
|
||||
if self
|
||||
.routing_table()
|
||||
.matches_own_node_id_key(&key.clone().into())
|
||||
{
|
||||
if self.routing_table().public_keys().contains(key) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -1893,7 +1934,7 @@ impl RouteSpecStore {
|
|||
|
||||
Ok(RouteId::new(
|
||||
vcrypto.kind(),
|
||||
BareRouteId::new(vcrypto.generate_hash(&pkbytes).bytes()),
|
||||
BareRouteId::new(vcrypto.generate_hash(&pkbytes).ref_value()),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -1927,7 +1968,7 @@ impl RouteSpecStore {
|
|||
|
||||
Ok(RouteId::new(
|
||||
vcrypto.kind(),
|
||||
BareRouteId::new(vcrypto.generate_hash(&pkbytes).bytes()),
|
||||
BareRouteId::new(vcrypto.generate_hash(&pkbytes).ref_value()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ fn _get_route_permutation_count(hop_count: usize) -> usize {
|
|||
// hop_count = 4 -> 3! -> 6
|
||||
(3..hop_count).fold(2usize, |acc, x| acc * x)
|
||||
}
|
||||
pub type PermReturnType = (Vec<usize>, bool);
|
||||
pub type PermReturnType = (Vec<usize>, SequenceOrderingSet);
|
||||
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
|
||||
|
|
|
@ -2,18 +2,16 @@ use super::*;
|
|||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RouteSpecDetail {
|
||||
/// Crypto kind
|
||||
pub crypto_kind: CryptoKind,
|
||||
/// Secret key
|
||||
pub secret_key: BareSecretKey,
|
||||
/// Route hops (node id keys)
|
||||
pub secret_key: SecretKey,
|
||||
/// Route hop node ids
|
||||
pub hops: Vec<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RouteSetSpecDetail {
|
||||
/// Route set per crypto kind
|
||||
route_set: BTreeMap<BarePublicKey, RouteSpecDetail>,
|
||||
/// Routes in the set
|
||||
route_set: BTreeMap<PublicKey, RouteSpecDetail>,
|
||||
/// Route noderefs
|
||||
#[serde(skip)]
|
||||
hop_node_refs: Vec<NodeRef>,
|
||||
|
@ -26,7 +24,7 @@ pub struct RouteSetSpecDetail {
|
|||
/// Stability preference (prefer reliable nodes over faster)
|
||||
stability: Stability,
|
||||
/// Sequencing capability (connection oriented protocols vs datagram)
|
||||
can_do_sequenced: bool,
|
||||
orderings: SequenceOrderingSet,
|
||||
/// Stats
|
||||
stats: RouteStats,
|
||||
/// Automatically allocated route vs manually allocated route
|
||||
|
@ -36,11 +34,11 @@ pub struct RouteSetSpecDetail {
|
|||
impl RouteSetSpecDetail {
|
||||
pub fn new(
|
||||
cur_ts: Timestamp,
|
||||
route_set: BTreeMap<BarePublicKey, RouteSpecDetail>,
|
||||
route_set: BTreeMap<PublicKey, RouteSpecDetail>,
|
||||
hop_node_refs: Vec<NodeRef>,
|
||||
directions: DirectionSet,
|
||||
stability: Stability,
|
||||
can_do_sequenced: bool,
|
||||
orderings: SequenceOrderingSet,
|
||||
automatic: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -49,32 +47,50 @@ impl RouteSetSpecDetail {
|
|||
published: false,
|
||||
directions,
|
||||
stability,
|
||||
can_do_sequenced,
|
||||
orderings,
|
||||
stats: RouteStats::new(cur_ts),
|
||||
automatic,
|
||||
}
|
||||
}
|
||||
pub fn get_route_by_key(&self, key: &BarePublicKey) -> Option<&RouteSpecDetail> {
|
||||
#[expect(dead_code)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.route_set.len()
|
||||
}
|
||||
#[expect(dead_code)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.route_set.is_empty()
|
||||
}
|
||||
pub fn get_route_by_key(&self, key: &PublicKey) -> Option<&RouteSpecDetail> {
|
||||
self.route_set.get(key)
|
||||
}
|
||||
pub fn get_route_set_keys(&self) -> PublicKeyGroup {
|
||||
let mut tks = PublicKeyGroup::new();
|
||||
for (k, v) in &self.route_set {
|
||||
tks.add(PublicKey::new(v.crypto_kind, k.clone()));
|
||||
for k in self.route_set.keys() {
|
||||
tks.add(k.clone());
|
||||
}
|
||||
tks
|
||||
}
|
||||
pub fn get_best_route_set_key(&self) -> Option<BarePublicKey> {
|
||||
self.get_route_set_keys().best().map(|k| k.value())
|
||||
pub fn get_best_route_set_key(&self) -> Option<PublicKey> {
|
||||
self.get_route_set_keys().first().cloned()
|
||||
}
|
||||
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<'_, BarePublicKey, RouteSpecDetail> {
|
||||
) -> alloc::collections::btree_map::Iter<'_, PublicKey, RouteSpecDetail> {
|
||||
self.route_set.iter()
|
||||
}
|
||||
#[expect(dead_code)]
|
||||
pub fn iter_route_set_mut(
|
||||
&mut self,
|
||||
) -> alloc::collections::btree_map::IterMut<'_, PublicKey, RouteSpecDetail> {
|
||||
self.route_set.iter_mut()
|
||||
}
|
||||
#[expect(dead_code)]
|
||||
pub fn remove_route(&mut self, key: &PublicKey) {
|
||||
self.route_set.remove(key);
|
||||
}
|
||||
pub fn get_stats(&self) -> &RouteStats {
|
||||
&self.stats
|
||||
}
|
||||
|
@ -90,7 +106,7 @@ impl RouteSetSpecDetail {
|
|||
pub fn hop_count(&self) -> usize {
|
||||
self.hop_node_refs.len()
|
||||
}
|
||||
pub fn hops_node_refs(&self) -> Vec<NodeRef> {
|
||||
pub fn hop_node_refs(&self) -> Vec<NodeRef> {
|
||||
self.hop_node_refs.clone()
|
||||
}
|
||||
pub fn hop_node_ref(&self, idx: usize) -> Option<NodeRef> {
|
||||
|
@ -103,16 +119,17 @@ impl RouteSetSpecDetail {
|
|||
self.directions
|
||||
}
|
||||
pub fn is_sequencing_match(&self, sequencing: Sequencing) -> bool {
|
||||
match sequencing {
|
||||
Sequencing::NoPreference => true,
|
||||
Sequencing::PreferOrdered => true,
|
||||
Sequencing::EnsureOrdered => self.can_do_sequenced,
|
||||
for ordering in self.orderings.iter() {
|
||||
if sequencing.matches_ordering(ordering) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
pub fn contains_nodes(&self, nodes: &[NodeId]) -> bool {
|
||||
for tk in nodes {
|
||||
for rsd in self.route_set.values() {
|
||||
if rsd.crypto_kind == tk.kind() && rsd.hops.contains(tk) {
|
||||
if rsd.hops.contains(tk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -122,23 +139,15 @@ impl RouteSetSpecDetail {
|
|||
pub fn is_automatic(&self) -> bool {
|
||||
self.automatic
|
||||
}
|
||||
|
||||
/// Generate a key for the cache that can be used to uniquely identify this route's contents
|
||||
pub fn make_cache_key(&self, rti: &RoutingTableInner) -> Option<Vec<u8>> {
|
||||
pub fn make_cache_key(&self, rti: &RoutingTableInner) -> Vec<u8> {
|
||||
let hops = &self.hop_node_refs;
|
||||
|
||||
let mut cachelen = 0usize;
|
||||
let mut nodebytes = Vec::<BareNodeId>::with_capacity(hops.len());
|
||||
let mut cache: Vec<u8> = Vec::with_capacity(hops.len() * 32); // xxx hack: this code is going away soon anyway
|
||||
for hop in hops {
|
||||
let b = hop.locked(rti).best_node_id()?.value();
|
||||
cachelen += b.len();
|
||||
nodebytes.push(b);
|
||||
if let Some(b) = hop.locked(rti).best_node_id() {
|
||||
cache.extend_from_slice(b.ref_value());
|
||||
}
|
||||
}
|
||||
let mut cache: Vec<u8> = Vec::with_capacity(cachelen);
|
||||
for b in nodebytes {
|
||||
cache.extend_from_slice(&b);
|
||||
}
|
||||
|
||||
Some(cache)
|
||||
cache
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ impl_veilid_log_facility!("rtab");
|
|||
// Compiled route key for caching
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct CompiledRouteCacheKey {
|
||||
sr_pubkey: BarePublicKey,
|
||||
pr_pubkey: BarePublicKey,
|
||||
sr_pubkey: PublicKey,
|
||||
pr_pubkey: PublicKey,
|
||||
}
|
||||
|
||||
/// Compiled route (safety route + private route)
|
||||
|
@ -14,7 +14,7 @@ pub struct CompiledRoute {
|
|||
/// The safety route attached to the private route
|
||||
pub safety_route: SafetyRoute,
|
||||
/// The secret used to encrypt the message payload
|
||||
pub secret: BareSecretKey,
|
||||
pub secret: SecretKey,
|
||||
/// The node ref to the first hop in the compiled route
|
||||
/// filtered to the safetyselection it was compiled with
|
||||
pub first_hop: FilteredNodeRef,
|
||||
|
@ -34,7 +34,7 @@ pub struct RouteSpecStoreCache {
|
|||
/// Remote private routes we've imported and statistics
|
||||
remote_private_route_set_cache: LruCache<RouteId, RemotePrivateRouteInfo>,
|
||||
/// Remote private route ids indexed by route's public key
|
||||
remote_private_routes_by_key: HashMap<BarePublicKey, RouteId>,
|
||||
remote_private_routes_by_key: HashMap<PublicKey, RouteId>,
|
||||
/// Compiled route cache
|
||||
compiled_route_cache: LruCache<CompiledRouteCacheKey, SafetyRoute>,
|
||||
/// List of dead allocated routes
|
||||
|
@ -62,9 +62,7 @@ impl RouteSpecStoreCache {
|
|||
|
||||
/// add an allocated route set to our cache via its cache key
|
||||
pub fn add_to_cache(&mut self, rti: &RoutingTableInner, rssd: &RouteSetSpecDetail) {
|
||||
let Some(cache_key) = rssd.make_cache_key(rti) else {
|
||||
panic!("all routes should have a cache key");
|
||||
};
|
||||
let cache_key = rssd.make_cache_key(rti);
|
||||
if !self.hop_cache.insert(cache_key) {
|
||||
panic!("route should never be inserted twice");
|
||||
}
|
||||
|
@ -94,9 +92,7 @@ impl RouteSpecStoreCache {
|
|||
id: RouteId,
|
||||
rssd: &RouteSetSpecDetail,
|
||||
) -> bool {
|
||||
let Some(cache_key) = rssd.make_cache_key(rti) else {
|
||||
panic!("all routes should have a cache key");
|
||||
};
|
||||
let cache_key = rssd.make_cache_key(rti);
|
||||
|
||||
// Remove from hop cache
|
||||
if !self.hop_cache.remove(&cache_key) {
|
||||
|
@ -161,7 +157,7 @@ impl RouteSpecStoreCache {
|
|||
// also store in id by key table
|
||||
for private_route in rprinfo.get_private_routes() {
|
||||
self.remote_private_routes_by_key
|
||||
.insert(private_route.public_key.value(), id.clone());
|
||||
.insert(private_route.public_key.clone(), id.clone());
|
||||
}
|
||||
|
||||
let mut dead = None;
|
||||
|
@ -179,9 +175,9 @@ impl RouteSpecStoreCache {
|
|||
for dead_private_route in dead_rpri.get_private_routes() {
|
||||
let _ = self
|
||||
.remote_private_routes_by_key
|
||||
.remove(dead_private_route.public_key.ref_value())
|
||||
.remove(&dead_private_route.public_key)
|
||||
.unwrap();
|
||||
self.invalidate_compiled_route_cache(dead_private_route.public_key.ref_value());
|
||||
self.invalidate_compiled_route_cache(&dead_private_route.public_key);
|
||||
}
|
||||
self.dead_remote_routes.push(dead_id);
|
||||
}
|
||||
|
@ -265,7 +261,7 @@ impl RouteSpecStoreCache {
|
|||
}
|
||||
|
||||
/// look up a remote private route id by one of the route public keys
|
||||
pub fn get_remote_private_route_id_by_key(&self, key: &BarePublicKey) -> Option<RouteId> {
|
||||
pub fn get_remote_private_route_id_by_key(&self, key: &PublicKey) -> Option<RouteId> {
|
||||
self.remote_private_routes_by_key.get(key).cloned()
|
||||
}
|
||||
|
||||
|
@ -307,22 +303,18 @@ impl RouteSpecStoreCache {
|
|||
for private_route in rprinfo.get_private_routes() {
|
||||
let _ = self
|
||||
.remote_private_routes_by_key
|
||||
.remove(private_route.public_key.ref_value())
|
||||
.remove(&private_route.public_key)
|
||||
.unwrap();
|
||||
self.invalidate_compiled_route_cache(private_route.public_key.ref_value());
|
||||
self.invalidate_compiled_route_cache(&private_route.public_key);
|
||||
}
|
||||
self.dead_remote_routes.push(id);
|
||||
true
|
||||
}
|
||||
|
||||
/// Stores a compiled 'safety + private' route so we don't have to compile it again later
|
||||
pub fn add_to_compiled_route_cache(
|
||||
&mut self,
|
||||
pr_pubkey: BarePublicKey,
|
||||
safety_route: SafetyRoute,
|
||||
) {
|
||||
pub fn add_to_compiled_route_cache(&mut self, pr_pubkey: PublicKey, safety_route: SafetyRoute) {
|
||||
let key = CompiledRouteCacheKey {
|
||||
sr_pubkey: safety_route.public_key.value(),
|
||||
sr_pubkey: safety_route.public_key.clone(),
|
||||
pr_pubkey: pr_pubkey.clone(),
|
||||
};
|
||||
|
||||
|
@ -334,8 +326,8 @@ impl RouteSpecStoreCache {
|
|||
/// Looks up an existing compiled route from the safety and private route components
|
||||
pub fn lookup_compiled_route_cache(
|
||||
&mut self,
|
||||
sr_pubkey: BarePublicKey,
|
||||
pr_pubkey: BarePublicKey,
|
||||
sr_pubkey: PublicKey,
|
||||
pr_pubkey: PublicKey,
|
||||
) -> Option<SafetyRoute> {
|
||||
let key = CompiledRouteCacheKey {
|
||||
sr_pubkey,
|
||||
|
@ -345,7 +337,7 @@ impl RouteSpecStoreCache {
|
|||
}
|
||||
|
||||
/// When routes are dropped, they should be removed from the compiled route cache
|
||||
fn invalidate_compiled_route_cache(&mut self, dead_key: &BarePublicKey) {
|
||||
fn invalidate_compiled_route_cache(&mut self, dead_key: &PublicKey) {
|
||||
let mut dead_entries = Vec::new();
|
||||
for (k, _v) in self.compiled_route_cache.iter() {
|
||||
if k.sr_pubkey == *dead_key || k.pr_pubkey == *dead_key {
|
||||
|
|
|
@ -4,7 +4,7 @@ use super::*;
|
|||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub(super) struct RouteSpecStoreContent {
|
||||
/// All of the route sets we have allocated so far indexed by key (many to one)
|
||||
id_by_key: HashMap<BarePublicKey, RouteId>,
|
||||
id_by_key: HashMap<PublicKey, RouteId>,
|
||||
/// All of the route sets we have allocated so far
|
||||
details: HashMap<RouteId, RouteSetSpecDetail>,
|
||||
}
|
||||
|
@ -44,8 +44,9 @@ impl RouteSpecStoreContent {
|
|||
// Apply noderefs
|
||||
rssd.set_hop_node_refs(hop_node_refs);
|
||||
}
|
||||
|
||||
for id in dead_ids {
|
||||
veilid_log!(table_store trace "no entry, killing off private route: {}", id);
|
||||
veilid_log!(table_store trace "no entry, killing route set: {}", id);
|
||||
content.remove_detail(&id);
|
||||
}
|
||||
|
||||
|
@ -86,7 +87,7 @@ impl RouteSpecStoreContent {
|
|||
pub fn get_detail_mut(&mut self, id: &RouteId) -> Option<&mut RouteSetSpecDetail> {
|
||||
self.details.get_mut(id)
|
||||
}
|
||||
pub fn get_id_by_key(&self, key: &BarePublicKey) -> Option<RouteId> {
|
||||
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> {
|
||||
|
|
|
@ -840,6 +840,7 @@ impl RoutingTableInner {
|
|||
}
|
||||
|
||||
/// Resolve an existing routing table entry and call a function on its entry without using a noderef
|
||||
#[expect(dead_code)]
|
||||
pub fn with_node_entry<F, R>(&self, node_id: NodeId, f: F) -> Option<R>
|
||||
where
|
||||
F: FnOnce(Arc<BucketEntry>) -> R,
|
||||
|
@ -886,7 +887,7 @@ impl RoutingTableInner {
|
|||
let node_info = peer_info.node_info();
|
||||
let relay_ids = node_info.relay_ids();
|
||||
let node_ids = peer_info.node_ids().clone();
|
||||
if node_ids.contains_any(&relay_ids) {
|
||||
if node_ids.contains_any_from_slice(&relay_ids) {
|
||||
bail!("node can not be its own relay");
|
||||
}
|
||||
|
||||
|
@ -972,7 +973,7 @@ impl RoutingTableInner {
|
|||
"routing domains should be the same here",
|
||||
);
|
||||
let mut node_ids = old_pi.node_ids().clone();
|
||||
node_ids.add_all(new_pi.node_ids());
|
||||
node_ids.add_all_from_slice(new_pi.node_ids());
|
||||
(new_pi.routing_domain(), node_ids)
|
||||
}
|
||||
};
|
||||
|
@ -986,7 +987,7 @@ impl RoutingTableInner {
|
|||
.iter()
|
||||
.flat_map(|rdr| rdr.relay_node.locked(rti).node_ids().to_vec())
|
||||
.collect::<Vec<_>>();
|
||||
if node_ids.contains_any(&our_relay_node_ids) {
|
||||
if node_ids.contains_any_from_slice(&our_relay_node_ids) {
|
||||
rd.refresh();
|
||||
rd.publish_peer_info(rti);
|
||||
}
|
||||
|
@ -1102,6 +1103,7 @@ impl RoutingTableInner {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
#[expect(dead_code)]
|
||||
pub fn transform_to_peer_info(
|
||||
&self,
|
||||
routing_domain: RoutingDomain,
|
||||
|
@ -1260,7 +1262,7 @@ impl RoutingTableInner {
|
|||
pub fn find_preferred_closest_nodes<T, O>(
|
||||
&self,
|
||||
node_count: usize,
|
||||
hash_coordinate: HashDigest,
|
||||
hash_coordinate: HashCoordinate,
|
||||
mut filters: VecDeque<RoutingTableEntryFilter>,
|
||||
transform: T,
|
||||
) -> VeilidAPIResult<Vec<O>>
|
||||
|
@ -1272,10 +1274,6 @@ impl RoutingTableInner {
|
|||
|
||||
// Get the crypto kind
|
||||
let crypto_kind = hash_coordinate.kind();
|
||||
let crypto = self.crypto();
|
||||
let Some(vcrypto) = crypto.get(crypto_kind) else {
|
||||
apibail_generic!("invalid crypto kind");
|
||||
};
|
||||
|
||||
// Filter to ensure entries support the crypto kind in use
|
||||
// always filter out dead and punished nodes
|
||||
|
@ -1334,14 +1332,14 @@ impl RoutingTableInner {
|
|||
};
|
||||
|
||||
// distance is the next metric, closer nodes first
|
||||
let da = vcrypto.distance(
|
||||
&BareHashDigest::from(a_key.value()),
|
||||
hash_coordinate.ref_value(),
|
||||
);
|
||||
let db = vcrypto.distance(
|
||||
&BareHashDigest::from(b_key.value()),
|
||||
hash_coordinate.ref_value(),
|
||||
);
|
||||
let da = a_key
|
||||
.ref_value()
|
||||
.to_bare_hash_coordinate()
|
||||
.distance(hash_coordinate.ref_value());
|
||||
let db = b_key
|
||||
.ref_value()
|
||||
.to_bare_hash_coordinate()
|
||||
.distance(hash_coordinate.ref_value());
|
||||
da.cmp(&db)
|
||||
};
|
||||
|
||||
|
@ -1354,11 +1352,11 @@ impl RoutingTableInner {
|
|||
#[instrument(level = "trace", skip_all)]
|
||||
pub fn sort_and_clean_closest_noderefs(
|
||||
&self,
|
||||
node_id: HashDigest,
|
||||
hash_coordinate: HashCoordinate,
|
||||
closest_nodes: &[NodeRef],
|
||||
) -> Vec<NodeRef> {
|
||||
// Lock all noderefs
|
||||
let kind = node_id.kind();
|
||||
let kind = hash_coordinate.kind();
|
||||
let mut closest_nodes_locked: Vec<LockedNodeRef> = closest_nodes
|
||||
.iter()
|
||||
.filter_map(|nr| {
|
||||
|
@ -1372,8 +1370,7 @@ impl RoutingTableInner {
|
|||
.collect();
|
||||
|
||||
// Sort closest
|
||||
let crypto = self.crypto();
|
||||
let sort = make_closest_noderef_sort(&crypto, node_id);
|
||||
let sort = make_closest_noderef_sort(hash_coordinate);
|
||||
closest_nodes_locked.sort_by(sort);
|
||||
|
||||
// Unlock noderefs
|
||||
|
@ -1530,14 +1527,10 @@ impl RoutingTableInner {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub fn make_closest_noderef_sort<'a>(
|
||||
crypto: &'a Crypto,
|
||||
hash_coordinate: HashDigest,
|
||||
) -> impl Fn(&LockedNodeRef, &LockedNodeRef) -> core::cmp::Ordering + 'a {
|
||||
pub fn make_closest_noderef_sort(
|
||||
hash_coordinate: HashCoordinate,
|
||||
) -> impl Fn(&LockedNodeRef, &LockedNodeRef) -> core::cmp::Ordering {
|
||||
let kind = hash_coordinate.kind();
|
||||
// Get cryptoversion to check distance with
|
||||
let vcrypto = crypto.get(kind).unwrap();
|
||||
|
||||
move |a: &LockedNodeRef, b: &LockedNodeRef| -> core::cmp::Ordering {
|
||||
// same nodes are always the same
|
||||
|
@ -1552,38 +1545,42 @@ pub fn make_closest_noderef_sort<'a>(
|
|||
let b_key = b_entry.node_ids().get(kind).unwrap();
|
||||
|
||||
// distance is the next metric, closer nodes first
|
||||
let da = vcrypto.distance(
|
||||
&BareHashDigest::from(a_key.value()),
|
||||
hash_coordinate.ref_value(),
|
||||
);
|
||||
let db = vcrypto.distance(
|
||||
&BareHashDigest::from(b_key.value()),
|
||||
hash_coordinate.ref_value(),
|
||||
);
|
||||
let da = a_key
|
||||
.ref_value()
|
||||
.to_bare_hash_coordinate()
|
||||
.distance(hash_coordinate.ref_value());
|
||||
let db = b_key
|
||||
.ref_value()
|
||||
.to_bare_hash_coordinate()
|
||||
.distance(hash_coordinate.ref_value());
|
||||
da.cmp(&db)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_closest_node_id_sort(
|
||||
crypto: &Crypto,
|
||||
hash_coordinate: HashDigest,
|
||||
) -> impl Fn(&BareNodeId, &BareNodeId) -> core::cmp::Ordering + '_ {
|
||||
let kind = hash_coordinate.kind();
|
||||
// Get cryptoversion to check distance with
|
||||
let vcrypto = crypto.get(kind).unwrap();
|
||||
|
||||
pub fn make_closest_bare_node_id_sort(
|
||||
bare_hash_coordinate: BareHashCoordinate,
|
||||
) -> impl Fn(&BareNodeId, &BareNodeId) -> core::cmp::Ordering {
|
||||
move |a: &BareNodeId, b: &BareNodeId| -> core::cmp::Ordering {
|
||||
// distance is the next metric, closer nodes first
|
||||
let da = vcrypto.distance(
|
||||
&BareHashDigest::from(a.bytes()),
|
||||
hash_coordinate.ref_value(),
|
||||
);
|
||||
let db = vcrypto.distance(
|
||||
&BareHashDigest::from(b.bytes()),
|
||||
hash_coordinate.ref_value(),
|
||||
);
|
||||
let da = a.to_bare_hash_coordinate().distance(&bare_hash_coordinate);
|
||||
let db = b.to_bare_hash_coordinate().distance(&bare_hash_coordinate);
|
||||
da.cmp(&db)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_closest_node_id_sort(
|
||||
hash_coordinate: HashCoordinate,
|
||||
) -> impl Fn(&NodeId, &NodeId) -> core::cmp::Ordering {
|
||||
move |a: &NodeId, b: &NodeId| -> core::cmp::Ordering {
|
||||
let da = a
|
||||
.ref_value()
|
||||
.to_bare_hash_coordinate()
|
||||
.distance(hash_coordinate.ref_value());
|
||||
let db = b
|
||||
.ref_value()
|
||||
.to_bare_hash_coordinate()
|
||||
.distance(hash_coordinate.ref_value());
|
||||
da.cmp(&db)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ pub trait RoutingDomainDetail {
|
|||
) -> ContactMethod;
|
||||
|
||||
// Bootstrap peers
|
||||
#[expect(dead_code)]
|
||||
fn get_bootstrap_peers(&self) -> Vec<NodeRef>;
|
||||
fn clear_bootstrap_peers(&self);
|
||||
fn add_bootstrap_peer(&self, bootstrap_peer: NodeRef);
|
||||
|
|
|
@ -69,7 +69,10 @@ impl PublicInternetRoutingDomainDetail {
|
|||
// Note that relay_peer_info could be node_a, in which case a connection already exists
|
||||
// and we only get here if the connection had dropped, in which case node_a is unreachable until
|
||||
// it gets a new relay connection up
|
||||
if node_b_relay.node_ids().contains_any(ctx.peer_a.node_ids()) {
|
||||
if node_b_relay
|
||||
.node_ids()
|
||||
.contains_any_from_slice(ctx.peer_a.node_ids())
|
||||
{
|
||||
return Some(ContactMethod::Existing);
|
||||
}
|
||||
|
||||
|
@ -163,7 +166,10 @@ impl PublicInternetRoutingDomainDetail {
|
|||
// Note that relay_peer_info could be node_a, in which case a connection already exists
|
||||
// and we only get here if the connection had dropped, in which case node_b is unreachable until
|
||||
// it gets a new relay connection up
|
||||
if node_b_relay.node_ids().contains_any(ctx.peer_a.node_ids()) {
|
||||
if node_b_relay
|
||||
.node_ids()
|
||||
.contains_any_from_slice(ctx.peer_a.node_ids())
|
||||
{
|
||||
return Some(ContactMethod::Existing);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ pub struct RelayStatus {
|
|||
/// All protocol/address types requiring inbound relays for this node
|
||||
pub need_relay_protocols: HashSet<(ProtocolType, AddressType)>,
|
||||
/// Ordering modes we still need for relaying, per address type
|
||||
pub need_relay_orderings: HashSet<(bool, AddressType)>,
|
||||
pub need_relay_orderings: HashSet<(SequenceOrdering, AddressType)>,
|
||||
/// All protocol/address types we can offer inbound relaying for from this node
|
||||
#[expect(dead_code)]
|
||||
pub can_relay_protocols: HashSet<(ProtocolType, AddressType)>,
|
||||
|
@ -71,7 +71,7 @@ impl RelayStatus {
|
|||
let ordering_modes = node_info
|
||||
.outbound_protocols()
|
||||
.iter()
|
||||
.map(|x| x.is_ordered())
|
||||
.map(|x| x.sequence_ordering())
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
// Get the dial info list in preferred deterministic order
|
||||
|
@ -108,7 +108,7 @@ impl RelayStatus {
|
|||
for at in AddressTypeSet::all() {
|
||||
for pt in ProtocolTypeSet::all() {
|
||||
// if we can't use this protocol because we don't have its ordering mode enabled at all, then we should exclude it
|
||||
if !ordering_modes.contains(&pt.is_ordered()) {
|
||||
if !ordering_modes.contains(&pt.sequence_ordering()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ impl RelayStatus {
|
|||
can_relay_protocols.insert((pt, at));
|
||||
|
||||
// Note the relay ordering
|
||||
need_relay_orderings.remove(&(pt.is_ordered(), at));
|
||||
need_relay_orderings.remove(&(pt.sequence_ordering(), at));
|
||||
}
|
||||
|
||||
if needs_hairpin_nat_support || !direct_did_protoaddrs.contains(&(pt, at)) {
|
||||
|
@ -228,7 +228,7 @@ impl RelayStatus {
|
|||
// Determine for this relay, if there are dialinfo that are reachable with our node's
|
||||
// dialinfo filter, and which ordering modes can be satisfied by those flows
|
||||
|
||||
let mut possible_ordering_modes = HashSet::<bool>::new();
|
||||
let mut possible_ordering_modes = SequenceOrderingSet::new();
|
||||
for did in &dial_info_list {
|
||||
if did.class.requires_signal() {
|
||||
continue;
|
||||
|
@ -238,7 +238,7 @@ impl RelayStatus {
|
|||
// If this dial info can be contacted directly, then it can be used for receiving
|
||||
// relaying and satsifying an ordering mode
|
||||
if did.dial_info.matches_filter(&self.dial_info_filter) {
|
||||
possible_ordering_modes.insert(didpa.0.is_ordered());
|
||||
possible_ordering_modes.insert(didpa.0.sequence_ordering());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +267,7 @@ impl RelayStatus {
|
|||
|
||||
// Mark this ordering mode as satisfied
|
||||
self.need_relay_orderings
|
||||
.remove(&(didpa.0.is_ordered(), didpa.1));
|
||||
.remove(&(didpa.0.sequence_ordering(), didpa.1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,8 +284,8 @@ impl RelayStatus {
|
|||
let mut add_ping = false;
|
||||
|
||||
// See if we should add the ping for ordering mode coverage
|
||||
let is_ordered = didpa.0.is_ordered();
|
||||
add_ping |= possible_ordering_modes.remove(&is_ordered);
|
||||
let ordering = didpa.0.sequence_ordering();
|
||||
add_ping |= possible_ordering_modes.remove(ordering);
|
||||
|
||||
// See if we should add the ping for low level port mapping coverage
|
||||
if let Some((llpt, port)) = self
|
||||
|
|
|
@ -1,318 +0,0 @@
|
|||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RelayPing {
|
||||
pub node_ref: FilteredNodeRef,
|
||||
}
|
||||
|
||||
impl PartialEq for RelayPing {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node_ref.equivalent(&other.node_ref)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for RelayPing {}
|
||||
|
||||
/// The current node's relaying capabilities and requirements
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RelayStatus {
|
||||
/// Routing domain this is for
|
||||
pub routing_domain: RoutingDomain,
|
||||
/// Low level port info for this node
|
||||
/// This is which ports are mapped externally that we may need keepalive pings for
|
||||
pub low_level_port_info: LowLevelPortInfo,
|
||||
/// This node's outbound dial info filter
|
||||
/// Used to determine if a relay's dialinfo is directly reachable
|
||||
pub dial_info_filter: DialInfoFilter,
|
||||
/// All protocol/address types requiring inbound relays for this node
|
||||
pub need_relay_protocols: HashSet<(ProtocolType, AddressType)>,
|
||||
/// Ordering modes we still need for relaying, per address type
|
||||
pub need_relay_orderings: HashSet<(bool, AddressType)>,
|
||||
/// All protocol/address types we can offer inbound relaying for from this node
|
||||
#[expect(dead_code)]
|
||||
pub can_relay_protocols: HashSet<(ProtocolType, AddressType)>,
|
||||
/// All the low level protocols and ports that require nat keepalive pings
|
||||
pub wants_nat_keepalives: LowLevelProtocolPorts,
|
||||
/// All of the relays and their configuration currently included in our status
|
||||
pub relays: Vec<RoutingDomainRelay>,
|
||||
}
|
||||
|
||||
impl RelayStatus {
|
||||
pub fn new_from_routing_domain_detail(
|
||||
rdd: &dyn RoutingDomainDetail,
|
||||
needs_hairpin_nat_support: bool,
|
||||
) -> Self {
|
||||
// Make temporary nodeinfo without relay info or keys
|
||||
let node_info = NodeInfo::new(
|
||||
Timestamp::now(),
|
||||
VALID_ENVELOPE_VERSIONS.to_vec(),
|
||||
vec![],
|
||||
rdd.capabilities(),
|
||||
rdd.outbound_protocols(),
|
||||
rdd.address_types(),
|
||||
rdd.dial_info_details().clone(),
|
||||
vec![],
|
||||
);
|
||||
|
||||
Self::new_from_node_info(rdd.routing_domain(), &node_info, needs_hairpin_nat_support)
|
||||
}
|
||||
|
||||
fn new_from_node_info(
|
||||
routing_domain: RoutingDomain,
|
||||
node_info: &NodeInfo,
|
||||
needs_hairpin_nat_support: bool,
|
||||
) -> Self {
|
||||
let low_level_port_info = node_info.get_low_level_port_info();
|
||||
let dial_info_filter = DialInfoFilter::all()
|
||||
.with_protocol_type_set(node_info.outbound_protocols())
|
||||
.with_address_type_set(node_info.address_types());
|
||||
|
||||
// Determine ordering modes we need relaying for
|
||||
let ordering_modes = node_info
|
||||
.outbound_protocols()
|
||||
.iter()
|
||||
.map(|x| x.is_ordered())
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
// Get the dial info list in preferred deterministic order
|
||||
let mut dial_info_list = node_info.dial_info_detail_list().to_vec();
|
||||
dial_info_list.sort_by(DialInfoDetail::ordered_sequencing_sort);
|
||||
|
||||
// Figure out which dial info combinations we have that are direct-capable
|
||||
let mut direct_did_protoaddrs = HashSet::<(ProtocolType, AddressType)>::new();
|
||||
let mut wants_nat_keepalives = LowLevelProtocolPorts::new();
|
||||
for did in dial_info_list {
|
||||
if !did.class.requires_signal() {
|
||||
direct_did_protoaddrs
|
||||
.insert((did.dial_info.protocol_type(), did.dial_info.address_type()));
|
||||
}
|
||||
if did.class.wants_nat_keepalive() {
|
||||
wants_nat_keepalives.insert((
|
||||
did.dial_info.protocol_type().low_level_protocol_type(),
|
||||
did.dial_info.address_type(),
|
||||
did.dial_info.port(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate which address/protocol type combinations we require
|
||||
// relays for, and which we can offer relay support for
|
||||
let mut need_relay_protocols = HashSet::<(ProtocolType, AddressType)>::new();
|
||||
let mut can_relay_protocols = HashSet::<(ProtocolType, AddressType)>::new();
|
||||
for at in AddressTypeSet::all() {
|
||||
for pt in ProtocolTypeSet::all() {
|
||||
// if we can't use this protocol because we don't have its ordering mode enabled at all, then we should exclude it
|
||||
if !ordering_modes.contains(&pt.is_ordered()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if direct_did_protoaddrs.contains(&(pt, at)) {
|
||||
// We can relay this combination
|
||||
can_relay_protocols.insert((pt, at));
|
||||
}
|
||||
|
||||
if needs_hairpin_nat_support || !direct_did_protoaddrs.contains(&(pt, at)) {
|
||||
// We can't relay this, so we must need a relay for it ourselves
|
||||
// Or we want to allocate a hairpin NAT relay
|
||||
need_relay_protocols.insert((pt, at));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the ordering modes per address type we need, at a minimum, to be able to publish peer info
|
||||
let need_relay_orderings = AddressTypeSet::all()
|
||||
.iter()
|
||||
.flat_map(|at| ordering_modes.iter().map(move |om| (*om, at)))
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
RelayStatus {
|
||||
routing_domain,
|
||||
low_level_port_info,
|
||||
dial_info_filter,
|
||||
need_relay_protocols,
|
||||
need_relay_orderings,
|
||||
can_relay_protocols,
|
||||
wants_nat_keepalives,
|
||||
relays: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if we would like more relays
|
||||
pub fn wants_more_relays(&self) -> bool {
|
||||
!self.need_relay_protocols.is_empty() || !self.wants_nat_keepalives.is_empty()
|
||||
}
|
||||
|
||||
/// Check if we need more relays before publication
|
||||
pub fn needs_more_relays(&self) -> bool {
|
||||
!self.need_relay_orderings.is_empty() || !self.wants_nat_keepalives.is_empty()
|
||||
}
|
||||
|
||||
/// Get the routing domain relays list when we're done
|
||||
pub fn get_sorted_relays_list(&self) -> Vec<RoutingDomainRelay> {
|
||||
let mut relays = self.relays.clone();
|
||||
|
||||
// Sort things in order of relay preference, using least-capable relays first
|
||||
relays.sort_by(|ardr, brdr| {
|
||||
// Get address types and protocol types to sort by
|
||||
let mut aats = AddressTypeSet::new();
|
||||
let mut apts = ProtocolTypeSet::new();
|
||||
for did in &ardr.dial_info_details {
|
||||
aats |= did.dial_info.address_type();
|
||||
apts |= did.dial_info.protocol_type();
|
||||
}
|
||||
|
||||
let mut bats = AddressTypeSet::new();
|
||||
let mut bpts = ProtocolTypeSet::new();
|
||||
for did in &brdr.dial_info_details {
|
||||
bats |= did.dial_info.address_type();
|
||||
bpts |= did.dial_info.protocol_type();
|
||||
}
|
||||
|
||||
// Compare by address type set first (fewer address types is less)
|
||||
let c = aats.len().cmp(&bats.len());
|
||||
if c != cmp::Ordering::Equal {
|
||||
return c;
|
||||
}
|
||||
for at in AddressTypeSet::all() {
|
||||
let a = aats.contains(at);
|
||||
let b = bats.contains(at);
|
||||
let c = a.cmp(&b);
|
||||
if c != cmp::Ordering::Equal {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare by protocol types set second (fewer protocol types is less)
|
||||
let c = apts.len().cmp(&bpts.len());
|
||||
if c != cmp::Ordering::Equal {
|
||||
return c;
|
||||
}
|
||||
for pt in ProtocolTypeSet::all() {
|
||||
let a = apts.contains(pt);
|
||||
let b = bpts.contains(pt);
|
||||
let c = a.cmp(&b);
|
||||
if c != cmp::Ordering::Equal {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
// Then just compare by node id lists, so things are stable
|
||||
let a_nodes = ardr.relay_node.node_ids().to_vec();
|
||||
let b_nodes = brdr.relay_node.node_ids().to_vec();
|
||||
|
||||
a_nodes.cmp(&b_nodes)
|
||||
});
|
||||
relays
|
||||
}
|
||||
|
||||
/// Remove a relay's capabilities from our current requirements and determine which
|
||||
/// pings should be performed.
|
||||
/// Returns true if the requirements changed, or false if applying the relay had no effect
|
||||
pub fn apply_relay(&mut self, mut relay: RoutingDomainRelay) -> bool {
|
||||
// Make sure this relay is the correct routing domain and has peer info
|
||||
let Some(relay_peer_info) = relay.relay_node.get_peer_info(self.routing_domain) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Clear out the dial info details and the pings because we'll add new ones
|
||||
relay.dial_info_details.clear();
|
||||
relay.pings.clear();
|
||||
|
||||
// For all for the relay's dial info, see if it matches a protocol+address type we need covered
|
||||
let mut dial_info_list = relay_peer_info.node_info().dial_info_detail_list().to_vec();
|
||||
dial_info_list.sort_by(DialInfoDetail::ordered_sequencing_sort);
|
||||
|
||||
// Determine for this relay, if there are dialinfo that are reachable with our node's
|
||||
// dialinfo filter, and which ordering modes can be satisfied by those flows
|
||||
|
||||
let mut possible_ordering_modes = HashSet::<bool>::new();
|
||||
for did in &dial_info_list {
|
||||
if did.class.requires_signal() {
|
||||
continue;
|
||||
}
|
||||
let didpa = (did.dial_info.protocol_type(), did.dial_info.address_type());
|
||||
|
||||
// If this dial info can be contacted directly, then it can be used for receiving
|
||||
// relaying and satsifying an ordering mode
|
||||
if did.dial_info.matches_filter(&self.dial_info_filter) {
|
||||
possible_ordering_modes.insert(didpa.0.is_ordered());
|
||||
}
|
||||
}
|
||||
|
||||
// If we did not get a single ordering mode we need for relaying, then this relay is disqualified
|
||||
// because we can't connect to it with our outbound protocols/address types directly
|
||||
if possible_ordering_modes.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut useful = false;
|
||||
|
||||
// Determine relay dial infos we can use from this relay out of our set of needed relay combinations
|
||||
// Builds up a set of needed ordering modes to keep flows open for the dial infos we are getting relayed
|
||||
for did in &dial_info_list {
|
||||
if did.class.requires_signal() {
|
||||
continue;
|
||||
}
|
||||
let didpa = (did.dial_info.protocol_type(), did.dial_info.address_type());
|
||||
|
||||
if self.need_relay_protocols.remove(&didpa) {
|
||||
// Still needed this protocol+address type
|
||||
useful = true;
|
||||
|
||||
// Mark this dial info as one we're using
|
||||
relay.dial_info_details.push(did.clone());
|
||||
|
||||
// Mark this ordering mode as needed
|
||||
self.need_relay_orderings
|
||||
.remove(&(didpa.0.is_ordered(), didpa.1));
|
||||
}
|
||||
}
|
||||
|
||||
// Collect pings we can use from this relay
|
||||
for did in &dial_info_list {
|
||||
if did.class.requires_signal() {
|
||||
continue;
|
||||
}
|
||||
let didpa = (did.dial_info.protocol_type(), did.dial_info.address_type());
|
||||
|
||||
// If this dial info can be contacted directly, then it is a ping candidate
|
||||
if did.dial_info.matches_filter(&self.dial_info_filter) {
|
||||
// See if we should add this ping
|
||||
let mut add_ping = false;
|
||||
|
||||
// See if we should add the ping for ordering mode coverage
|
||||
let is_ordered = didpa.0.is_ordered();
|
||||
add_ping |= possible_ordering_modes.remove(&is_ordered);
|
||||
|
||||
// See if we should add the ping for low level port mapping coverage
|
||||
if let Some((llpt, port)) = self
|
||||
.low_level_port_info
|
||||
.protocol_to_port
|
||||
.get(&didpa)
|
||||
.copied()
|
||||
{
|
||||
let wnk = (llpt, didpa.1, port);
|
||||
add_ping |= self.wants_nat_keepalives.remove(&wnk);
|
||||
}
|
||||
|
||||
// Add the ping if we determined we could use it
|
||||
if add_ping {
|
||||
relay.pings.push(RelayPing {
|
||||
node_ref: relay.relay_node.unfiltered().custom_filtered(
|
||||
NodeRefFilter::new()
|
||||
.with_routing_domain(self.routing_domain)
|
||||
.with_dial_info_filter(did.dial_info.make_filter()),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a relay info to our list if it turned out to be useful
|
||||
if useful {
|
||||
self.relays.push(relay);
|
||||
}
|
||||
|
||||
useful
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ impl RoutingTable {
|
|||
let noderefs = self
|
||||
.find_preferred_closest_nodes(
|
||||
CLOSEST_PEERS_REQUEST_COUNT,
|
||||
self_node_id.into(),
|
||||
self_node_id.to_hash_coordinate(),
|
||||
filters,
|
||||
|_rti, entry: Option<Arc<BucketEntry>>| {
|
||||
NodeRef::new(self.registry(), entry.unwrap().clone())
|
||||
|
|
|
@ -16,7 +16,6 @@ impl RoutingTable {
|
|||
_last_ts: Timestamp,
|
||||
cur_ts: Timestamp,
|
||||
) -> EyreResult<()> {
|
||||
let crypto = self.crypto();
|
||||
let kick_queue: Vec<BucketIndex> = core::mem::take(&mut *self.kick_queue.lock())
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
@ -30,7 +29,8 @@ impl RoutingTable {
|
|||
let Some(buckets) = inner.buckets.get(&kind) else {
|
||||
continue;
|
||||
};
|
||||
let sort = make_closest_node_id_sort(&crypto, our_node_id.into());
|
||||
let sort =
|
||||
make_closest_bare_node_id_sort(our_node_id.ref_value().to_bare_hash_coordinate());
|
||||
|
||||
let mut closest_peers = BTreeSet::<BareNodeId>::new();
|
||||
let mut closest_unreliable_count = 0usize;
|
||||
|
|
|
@ -274,10 +274,7 @@ impl RoutingTable {
|
|||
let ordering_modes = self
|
||||
.get_current_peer_info(RoutingDomain::PublicInternet)
|
||||
.node_info()
|
||||
.outbound_protocols()
|
||||
.iter()
|
||||
.map(|x| x.is_ordered())
|
||||
.collect::<HashSet<_>>();
|
||||
.outbound_sequence_orderings();
|
||||
|
||||
// Just do a single ping with the best protocol for all the other nodes to check for liveness
|
||||
for nr in node_refs {
|
||||
|
@ -286,11 +283,8 @@ impl RoutingTable {
|
|||
|
||||
let all_noderefs = if nr.operate(|_rti, e| !relay_node_filter(e)) {
|
||||
let mut nrs = vec![];
|
||||
if ordering_modes.contains(&false) {
|
||||
nrs.push(nr.sequencing_clone(Sequencing::NoPreference));
|
||||
}
|
||||
if ordering_modes.contains(&true) {
|
||||
nrs.push(nr.sequencing_clone(Sequencing::EnsureOrdered));
|
||||
for ordering in ordering_modes {
|
||||
nrs.push(nr.sequencing_clone(ordering.strict_sequencing()));
|
||||
}
|
||||
nrs
|
||||
} else {
|
||||
|
|
|
@ -194,10 +194,7 @@ impl RoutingTable {
|
|||
let mut local_unpublished_route_count = 0usize;
|
||||
|
||||
self.route_spec_store().list_allocated_routes(|_k, v| {
|
||||
if !v.is_published()
|
||||
&& v.hop_count() == default_route_hop_count
|
||||
&& v.get_route_set_keys().kinds() == VALID_CRYPTO_KINDS
|
||||
{
|
||||
if !v.is_published() && v.hop_count() == default_route_hop_count {
|
||||
local_unpublished_route_count += 1;
|
||||
}
|
||||
Option::<()>::None
|
||||
|
|
|
@ -144,7 +144,7 @@ impl RoutingTable {
|
|||
if rdr
|
||||
.relay_node
|
||||
.node_ids()
|
||||
.contains_any(outbound_relay_peerinfo.node_ids())
|
||||
.contains_any_from_slice(outbound_relay_peerinfo.node_ids())
|
||||
{
|
||||
has_outbound_relay = true;
|
||||
true
|
||||
|
@ -282,7 +282,7 @@ impl RoutingTable {
|
|||
// Exclude any nodes that are relaying directly through us
|
||||
if own_peer_info
|
||||
.node_ids()
|
||||
.contains_any(&peer_info.node_info().relay_ids())
|
||||
.contains_any_from_slice(&peer_info.node_info().relay_ids())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ pub fn fix_typed_node_id_group(valid_kinds: bool, unknown: bool) -> NodeIdGroup
|
|||
});
|
||||
}
|
||||
if unknown {
|
||||
tks.add(fix_typed_node_id(CryptoKind([1, 2, 3, 4]), 0));
|
||||
tks.add(fix_typed_node_id(CryptoKind::new([1, 2, 3, 4]), 0));
|
||||
}
|
||||
tks
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ pub fn fix_peer_info(
|
|||
) -> VeilidAPIResult<PeerInfo> {
|
||||
let node_info = NodeInfo::new(
|
||||
Timestamp::new(0),
|
||||
vec![ENVELOPE_VERSION_VLD0],
|
||||
vec![ENVELOPE_VERSION_ENV0],
|
||||
crypto_info_list,
|
||||
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
|
||||
ProtocolTypeSet::new(),
|
||||
|
@ -152,7 +152,7 @@ pub fn fix_unsigned_peer_info(
|
|||
) -> VeilidAPIResult<PeerInfo> {
|
||||
let node_info = NodeInfo::new(
|
||||
Timestamp::new(0),
|
||||
vec![ENVELOPE_VERSION_VLD0],
|
||||
vec![ENVELOPE_VERSION_ENV0],
|
||||
crypto_info_list,
|
||||
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
|
||||
ProtocolTypeSet::new(),
|
||||
|
|
60
veilid-core/src/routing_table/types/hash_coordinate.rs
Normal file
60
veilid-core/src/routing_table/types/hash_coordinate.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use super::*;
|
||||
|
||||
pub const HASH_COORDINATE_LENGTH: usize = 32;
|
||||
|
||||
// Internal types
|
||||
|
||||
impl HashCoordinate {
|
||||
pub(crate) fn distance(&self, other: &HashCoordinate) -> HashDistance {
|
||||
assert_eq!(self.kind(), other.kind());
|
||||
self.ref_value().distance(other.ref_value())
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeId {
|
||||
pub(crate) fn to_hash_coordinate(&self) -> HashCoordinate {
|
||||
HashCoordinate::new(self.kind(), self.ref_value().to_bare_hash_coordinate())
|
||||
}
|
||||
}
|
||||
impl BareNodeId {
|
||||
pub(crate) fn to_bare_hash_coordinate(&self) -> BareHashCoordinate {
|
||||
BareHashCoordinate::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl RecordKey {
|
||||
pub(crate) fn to_hash_coordinate(&self) -> HashCoordinate {
|
||||
HashCoordinate::new(self.kind(), self.ref_value().to_bare_hash_coordinate())
|
||||
}
|
||||
}
|
||||
impl BareRecordKey {
|
||||
pub(crate) fn to_bare_hash_coordinate(&self) -> BareHashCoordinate {
|
||||
BareHashCoordinate::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl HashDigest {
|
||||
pub(crate) fn to_hash_coordinate(&self) -> HashCoordinate {
|
||||
HashCoordinate::new(self.kind(), self.ref_value().to_bare_hash_coordinate())
|
||||
}
|
||||
}
|
||||
impl BareHashDigest {
|
||||
pub(crate) fn to_bare_hash_coordinate(&self) -> BareHashCoordinate {
|
||||
BareHashCoordinate::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl BareHashCoordinate {
|
||||
pub(crate) fn distance(&self, other: &BareHashCoordinate) -> HashDistance {
|
||||
assert_eq!(self.len(), HASH_COORDINATE_LENGTH);
|
||||
assert_eq!(other.len(), HASH_COORDINATE_LENGTH);
|
||||
|
||||
let mut bytes = [0u8; HASH_COORDINATE_LENGTH];
|
||||
|
||||
(0..HASH_COORDINATE_LENGTH).for_each(|n| {
|
||||
bytes[n] = self[n] ^ other[n];
|
||||
});
|
||||
|
||||
HashDistance::new(&bytes)
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ mod direction;
|
|||
mod events;
|
||||
#[cfg(feature = "geolocation")]
|
||||
mod geolocation_info;
|
||||
mod hash_coordinate;
|
||||
mod low_level_port_info;
|
||||
mod node_info;
|
||||
mod node_status;
|
||||
|
@ -22,6 +23,7 @@ pub use direction::*;
|
|||
pub use events::*;
|
||||
#[cfg(feature = "geolocation")]
|
||||
pub use geolocation_info::*;
|
||||
pub use hash_coordinate::*;
|
||||
pub use low_level_port_info::*;
|
||||
pub use node_info::*;
|
||||
pub use node_status::*;
|
||||
|
|
|
@ -80,6 +80,13 @@ impl NodeInfo {
|
|||
pub fn outbound_protocols(&self) -> ProtocolTypeSet {
|
||||
self.outbound_protocols
|
||||
}
|
||||
pub fn outbound_sequence_orderings(&self) -> SequenceOrderingSet {
|
||||
self.outbound_protocols
|
||||
.iter()
|
||||
.map(|x| x.sequence_ordering())
|
||||
.collect::<SequenceOrderingSet>()
|
||||
}
|
||||
|
||||
pub fn address_types(&self) -> AddressTypeSet {
|
||||
self.address_types
|
||||
}
|
||||
|
@ -240,13 +247,8 @@ impl HasDialInfoDetailList for NodeInfo {
|
|||
fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool {
|
||||
// Check our dial info
|
||||
for did in self.dial_info_detail_list() {
|
||||
match sequencing {
|
||||
Sequencing::NoPreference | Sequencing::PreferOrdered => return true,
|
||||
Sequencing::EnsureOrdered => {
|
||||
if did.dial_info.protocol_type().is_ordered() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if sequencing.matches_ordering(did.dial_info.protocol_type().sequence_ordering()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Check our relays
|
||||
|
|
|
@ -76,7 +76,7 @@ impl PeerInfo {
|
|||
let signatures = SignatureGroup::from(crypto.generate_signatures(
|
||||
&node_info_message,
|
||||
&keypairs,
|
||||
|kp, sig| Signature::new(kp.kind(), sig),
|
||||
|_kp, sig| sig,
|
||||
)?);
|
||||
|
||||
// Extract node ids for convenience
|
||||
|
|
|
@ -89,13 +89,8 @@ impl HasDialInfoDetailList for RelayInfo {
|
|||
fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool {
|
||||
// Check our dial info
|
||||
for did in self.dial_info_detail_list() {
|
||||
match sequencing {
|
||||
Sequencing::NoPreference | Sequencing::PreferOrdered => return true,
|
||||
Sequencing::EnsureOrdered => {
|
||||
if did.dial_info.protocol_type().is_ordered() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if sequencing.matches_ordering(did.dial_info.protocol_type().sequence_ordering()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use super::*;
|
||||
|
||||
fourcc_type!(VeilidCapability);
|
||||
pub const CAP_ROUTE: VeilidCapability = VeilidCapability(*b"ROUT");
|
||||
pub const CAP_ROUTE: VeilidCapability = VeilidCapability::new(*b"ROUT");
|
||||
#[cfg(feature = "unstable-tunnels")]
|
||||
pub const CAP_TUNNEL: VeilidCapability = VeilidCapability(*b"TUNL");
|
||||
pub const CAP_SIGNAL: VeilidCapability = VeilidCapability(*b"SGNL");
|
||||
pub const CAP_RELAY: VeilidCapability = VeilidCapability(*b"RLAY");
|
||||
pub const CAP_VALIDATE_DIAL_INFO: VeilidCapability = VeilidCapability(*b"DIAL");
|
||||
pub const CAP_DHT: VeilidCapability = VeilidCapability(*b"DHTV");
|
||||
pub const CAP_APPMESSAGE: VeilidCapability = VeilidCapability(*b"APPM");
|
||||
pub const CAP_TUNNEL: VeilidCapability = VeilidCapability::new(*b"TUNL");
|
||||
pub const CAP_SIGNAL: VeilidCapability = VeilidCapability::new(*b"SGNL");
|
||||
pub const CAP_RELAY: VeilidCapability = VeilidCapability::new(*b"RLAY");
|
||||
pub const CAP_VALIDATE_DIAL_INFO: VeilidCapability = VeilidCapability::new(*b"DIAL");
|
||||
pub const CAP_DHT: VeilidCapability = VeilidCapability::new(*b"DHTV");
|
||||
pub const CAP_APPMESSAGE: VeilidCapability = VeilidCapability::new(*b"APPM");
|
||||
#[cfg(feature = "unstable-blockstore")]
|
||||
pub const CAP_BLOCKSTORE: VeilidCapability = VeilidCapability(*b"BLOC");
|
||||
pub const CAP_BLOCKSTORE: VeilidCapability = VeilidCapability::new(*b"BLOC");
|
||||
|
||||
pub const DISTANCE_METRIC_CAPABILITIES: &[VeilidCapability] = &[CAP_DHT];
|
||||
pub const CONNECTIVITY_CAPABILITIES: &[VeilidCapability] =
|
||||
|
|
|
@ -5,14 +5,14 @@ pub struct Answer<T> {
|
|||
/// Hpw long it took to get this answer
|
||||
pub _latency: TimestampDuration,
|
||||
/// The private route requested to receive the reply
|
||||
pub reply_private_route: Option<BarePublicKey>,
|
||||
pub reply_private_route: Option<PublicKey>,
|
||||
/// The answer itself
|
||||
pub answer: T,
|
||||
}
|
||||
impl<T> Answer<T> {
|
||||
pub fn new(
|
||||
latency: TimestampDuration,
|
||||
reply_private_route: Option<BarePublicKey>,
|
||||
reply_private_route: Option<PublicKey>,
|
||||
answer: T,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -22,7 +22,7 @@ macro_rules! define_typed_byte_data_coder {
|
|||
$capnp_name: &$rust_name,
|
||||
builder: &mut veilid_capnp::$capnp_name::Builder,
|
||||
) {
|
||||
builder.set_kind(u32::from_be_bytes($capnp_name.kind().0));
|
||||
builder.set_kind(u32::from($capnp_name.kind()));
|
||||
builder.set_value($capnp_name.ref_value());
|
||||
}
|
||||
}
|
||||
|
@ -69,4 +69,4 @@ define_typed_byte_data_coder!(route_id, RouteId);
|
|||
define_typed_byte_data_coder!(signature, Signature);
|
||||
|
||||
// Nonce
|
||||
define_untyped_byte_data_coder!(nonce, BareNonce);
|
||||
define_untyped_byte_data_coder!(nonce, Nonce);
|
||||
|
|
|
@ -146,7 +146,8 @@ pub fn encode_node_info(
|
|||
let capvec: Vec<u32> = node_info
|
||||
.capabilities()
|
||||
.iter()
|
||||
.map(|x| u32::from_be_bytes(x.0))
|
||||
.copied()
|
||||
.map(u32::from)
|
||||
.collect();
|
||||
|
||||
s.clone_from_slice(&capvec);
|
||||
|
|
|
@ -66,11 +66,7 @@ impl RPCOperationFindNodeQ {
|
|||
.reborrow()
|
||||
.init_capabilities(self.capabilities.len() as u32);
|
||||
if let Some(s) = cap_builder.as_slice() {
|
||||
let capvec: Vec<u32> = self
|
||||
.capabilities
|
||||
.iter()
|
||||
.map(|x| u32::from_be_bytes(x.0))
|
||||
.collect();
|
||||
let capvec: Vec<u32> = self.capabilities.iter().copied().map(u32::from).collect();
|
||||
|
||||
s.clone_from_slice(&capvec);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ pub(in crate::rpc_processor) struct RPCOperationReturnReceipt {
|
|||
|
||||
impl RPCOperationReturnReceipt {
|
||||
pub fn new(receipt: Vec<u8>) -> Result<Self, RPCError> {
|
||||
if receipt.len() < MIN_RECEIPT_SIZE {
|
||||
if receipt.len() < RCP0_MIN_RECEIPT_SIZE {
|
||||
return Err(RPCError::protocol("ReturnReceipt receipt too short to set"));
|
||||
}
|
||||
if receipt.len() > MAX_RECEIPT_SIZE {
|
||||
if receipt.len() > RCP0_MAX_RECEIPT_SIZE {
|
||||
return Err(RPCError::protocol("ReturnReceipt receipt too long to set"));
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ impl RPCOperationReturnReceipt {
|
|||
) -> Result<Self, RPCError> {
|
||||
rpc_ignore_missing_property!(reader, receipt);
|
||||
let rr = reader.get_receipt()?;
|
||||
rpc_ignore_min_max_len!(rr, MIN_RECEIPT_SIZE, MAX_RECEIPT_SIZE);
|
||||
rpc_ignore_min_max_len!(rr, RCP0_MIN_RECEIPT_SIZE, RCP0_MAX_RECEIPT_SIZE);
|
||||
|
||||
Ok(Self {
|
||||
receipt: rr.to_vec(),
|
||||
|
|
|
@ -5,7 +5,7 @@ pub(in crate::rpc_processor) struct RoutedOperation {
|
|||
routing_domain: RoutingDomain,
|
||||
sequencing: Sequencing,
|
||||
signatures: Vec<Signature>,
|
||||
nonce: BareNonce,
|
||||
nonce: Nonce,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ impl RoutedOperation {
|
|||
pub fn new(
|
||||
routing_domain: RoutingDomain,
|
||||
sequencing: Sequencing,
|
||||
nonce: BareNonce,
|
||||
nonce: Nonce,
|
||||
data: Vec<u8>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
@ -54,7 +54,7 @@ impl RoutedOperation {
|
|||
self.signatures.push(signature);
|
||||
}
|
||||
|
||||
pub fn nonce(&self) -> &BareNonce {
|
||||
pub fn nonce(&self) -> &Nonce {
|
||||
&self.nonce
|
||||
}
|
||||
pub fn data(&self) -> &[u8] {
|
||||
|
|
|
@ -9,12 +9,12 @@ pub(in crate::rpc_processor) struct RPCOperationValidateDialInfo {
|
|||
|
||||
impl RPCOperationValidateDialInfo {
|
||||
pub fn new(dial_info: DialInfo, receipt: Vec<u8>, redirect: bool) -> Result<Self, RPCError> {
|
||||
if receipt.len() < MIN_RECEIPT_SIZE {
|
||||
if receipt.len() < RCP0_MIN_RECEIPT_SIZE {
|
||||
return Err(RPCError::protocol(
|
||||
"ValidateDialInfo receipt too short to set",
|
||||
));
|
||||
}
|
||||
if receipt.len() > MAX_RECEIPT_SIZE {
|
||||
if receipt.len() > RCP0_MAX_RECEIPT_SIZE {
|
||||
return Err(RPCError::protocol(
|
||||
"ValidateDialInfo receipt too long to set",
|
||||
));
|
||||
|
@ -53,7 +53,7 @@ impl RPCOperationValidateDialInfo {
|
|||
|
||||
rpc_ignore_missing_property!(reader, receipt);
|
||||
let rcpt_reader = reader.get_receipt()?;
|
||||
rpc_ignore_min_max_len!(rcpt_reader, MIN_RECEIPT_SIZE, MAX_RECEIPT_SIZE);
|
||||
rpc_ignore_min_max_len!(rcpt_reader, RCP0_MIN_RECEIPT_SIZE, RCP0_MAX_RECEIPT_SIZE);
|
||||
|
||||
let receipt = rcpt_reader.to_vec();
|
||||
let redirect = reader.get_redirect();
|
||||
|
|
|
@ -34,16 +34,9 @@ impl RPCOperationWatchValueQ {
|
|||
}
|
||||
|
||||
let signature_data = Self::make_signature_data(&key, &subkeys, expiration, count, watch_id);
|
||||
let signature = Signature::new(
|
||||
vcrypto.kind(),
|
||||
vcrypto
|
||||
.sign(
|
||||
watcher.ref_value().ref_key(),
|
||||
watcher.ref_value().ref_secret(),
|
||||
&signature_data,
|
||||
)
|
||||
.map_err(RPCError::protocol)?,
|
||||
);
|
||||
let signature = vcrypto
|
||||
.sign(&watcher.key(), &watcher.secret(), &signature_data)
|
||||
.map_err(RPCError::protocol)?;
|
||||
|
||||
Ok(Self {
|
||||
key,
|
||||
|
@ -51,7 +44,7 @@ impl RPCOperationWatchValueQ {
|
|||
expiration,
|
||||
count,
|
||||
watch_id,
|
||||
watcher: PublicKey::new(watcher.kind(), watcher.ref_value().key()),
|
||||
watcher: watcher.key(),
|
||||
signature,
|
||||
})
|
||||
}
|
||||
|
@ -68,7 +61,7 @@ impl RPCOperationWatchValueQ {
|
|||
|
||||
let mut sig_data =
|
||||
Vec::with_capacity(key.ref_value().len() + 4 + (subkeys_ranges_len * 8) + 8 + 8);
|
||||
sig_data.extend_from_slice(&key.kind().0);
|
||||
sig_data.extend_from_slice(key.kind().bytes());
|
||||
sig_data.extend_from_slice(key.ref_value());
|
||||
for sk in subkeys.ranges() {
|
||||
sig_data.extend_from_slice(&sk.start().to_le_bytes());
|
||||
|
@ -96,11 +89,7 @@ impl RPCOperationWatchValueQ {
|
|||
self.watch_id,
|
||||
);
|
||||
if !vcrypto
|
||||
.verify(
|
||||
self.watcher.ref_value(),
|
||||
&sig_data,
|
||||
self.signature.ref_value(),
|
||||
)
|
||||
.verify(&self.watcher, &sig_data, &self.signature)
|
||||
.map_err(RPCError::protocol)?
|
||||
{
|
||||
return Err(RPCError::protocol("failed to validate watcher signature"));
|
||||
|
|
|
@ -391,7 +391,7 @@ impl RPCProcessor {
|
|||
.kind();
|
||||
|
||||
let mut avoid_nodes = relay.node_ids();
|
||||
avoid_nodes.add_all(&target.node_ids());
|
||||
avoid_nodes.add_all_from_slice(&target.node_ids());
|
||||
let pr_key = network_result_try!(rss
|
||||
.get_private_route_for_safety_spec(crypto_kind, safety_spec, &avoid_nodes,)
|
||||
.to_rpc_network_result()?);
|
||||
|
@ -430,7 +430,7 @@ impl RPCProcessor {
|
|||
|
||||
// Determine if we can use optimized nodeinfo
|
||||
let route_node = if rss.has_remote_private_route_seen_our_node_info(
|
||||
private_route.public_key.ref_value(),
|
||||
&private_route.public_key,
|
||||
&published_peer_info,
|
||||
) {
|
||||
RouteNode::NodeId(routing_table.node_id(crypto_kind))
|
||||
|
@ -440,7 +440,7 @@ impl RPCProcessor {
|
|||
|
||||
Ok(NetworkResult::value(RespondTo::PrivateRoute(
|
||||
PrivateRoute::new_stub(
|
||||
routing_table.node_id(crypto_kind).into(),
|
||||
routing_table.public_key(crypto_kind),
|
||||
route_node,
|
||||
),
|
||||
)))
|
||||
|
@ -450,12 +450,12 @@ impl RPCProcessor {
|
|||
|
||||
// Check for loopback test
|
||||
let opt_private_route_id =
|
||||
rss.get_route_id_for_key(private_route.public_key.ref_value());
|
||||
rss.get_route_id_for_key(&private_route.public_key);
|
||||
let pr_key = if opt_private_route_id.is_some()
|
||||
&& safety_spec.preferred_route == opt_private_route_id
|
||||
{
|
||||
// Private route is also safety route during loopback test
|
||||
private_route.public_key.value()
|
||||
private_route.public_key.clone()
|
||||
} else {
|
||||
// Get the private route to respond to that matches the safety route spec we sent the request with
|
||||
network_result_try!(rss
|
||||
|
|
|
@ -52,7 +52,6 @@ impl RPCError {
|
|||
pub fn map_network<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
|
||||
move |x| Self::Network(format!("{}: {}", message.to_string(), x.to_string()))
|
||||
}
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), expect(dead_code))]
|
||||
pub fn try_again<X: ToString>(x: X) -> Self {
|
||||
Self::TryAgain(x.to_string())
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ pub fn capability_fanout_peer_info_filter(caps: Vec<VeilidCapability>) -> Fanout
|
|||
/// in the given time
|
||||
pub(crate) struct FanoutCall<'a> {
|
||||
routing_table: &'a RoutingTable,
|
||||
hash_coordinate: HashDigest,
|
||||
hash_coordinate: HashCoordinate,
|
||||
node_count: usize,
|
||||
fanout_tasks: usize,
|
||||
consensus_count: usize,
|
||||
|
@ -166,7 +166,7 @@ impl<'a> FanoutCall<'a> {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
routing_table: &'a RoutingTable,
|
||||
hash_coordinate: HashDigest,
|
||||
hash_coordinate: HashCoordinate,
|
||||
node_count: usize,
|
||||
fanout_tasks: usize,
|
||||
consensus_count: usize,
|
||||
|
@ -259,10 +259,7 @@ impl<'a> FanoutCall<'a> {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", target = "fanout", skip_all)]
|
||||
async fn fanout_processor<'b>(
|
||||
&self,
|
||||
context: &Mutex<FanoutContext<'b>>,
|
||||
) -> Result<bool, RPCError> {
|
||||
async fn fanout_processor(&self, context: &Mutex<FanoutContext<'_>>) -> Result<bool, RPCError> {
|
||||
// Make a work request channel
|
||||
let (work_sender, work_receiver) = flume::bounded(1);
|
||||
|
||||
|
@ -407,27 +404,7 @@ impl<'a> FanoutCall<'a> {
|
|||
#[instrument(level = "trace", target = "fanout", skip_all)]
|
||||
pub async fn run(&self, init_fanout_queue: Vec<NodeRef>) -> Result<FanoutResult, RPCError> {
|
||||
// Create context for this run
|
||||
let crypto = self.routing_table.crypto();
|
||||
let Some(vcrypto) = crypto.get(self.hash_coordinate.kind()) else {
|
||||
return Err(RPCError::internal(
|
||||
"should not try this on crypto we don't support",
|
||||
));
|
||||
};
|
||||
let node_sort = Box::new(
|
||||
|a_key: &CryptoTyped<BareNodeId>,
|
||||
b_key: &CryptoTyped<BareNodeId>|
|
||||
-> core::cmp::Ordering {
|
||||
let da = vcrypto.distance(
|
||||
&BareHashDigest::from(a_key.value()),
|
||||
self.hash_coordinate.ref_value(),
|
||||
);
|
||||
let db = vcrypto.distance(
|
||||
&BareHashDigest::from(b_key.value()),
|
||||
self.hash_coordinate.ref_value(),
|
||||
);
|
||||
da.cmp(&db)
|
||||
},
|
||||
);
|
||||
let node_sort = Box::new(make_closest_node_id_sort(self.hash_coordinate.clone()));
|
||||
let context = Arc::new(Mutex::new(FanoutContext {
|
||||
fanout_queue: FanoutQueue::new(
|
||||
self.routing_table.registry(),
|
||||
|
|
|
@ -20,7 +20,7 @@ pub(in crate::rpc_processor) struct RPCMessageHeaderDetailSafetyRouted {
|
|||
/// Direct header
|
||||
pub direct: RPCMessageHeaderDetailDirect,
|
||||
/// Remote safety route used
|
||||
pub remote_safety_route: BarePublicKey,
|
||||
pub remote_safety_route: PublicKey,
|
||||
/// The sequencing used for this route
|
||||
pub sequencing: Sequencing,
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ pub(in crate::rpc_processor) struct RPCMessageHeaderDetailPrivateRouted {
|
|||
/// Direct header
|
||||
pub direct: RPCMessageHeaderDetailDirect,
|
||||
/// Remote safety route used (or possibly node id the case of no safety route)
|
||||
pub remote_safety_route: BarePublicKey,
|
||||
pub remote_safety_route: PublicKey,
|
||||
/// The private route we received the rpc over
|
||||
pub private_route: BarePublicKey,
|
||||
pub private_route: PublicKey,
|
||||
// The safety spec for replying to this private routed rpc
|
||||
pub safety_spec: SafetySpec,
|
||||
}
|
||||
|
|
|
@ -80,9 +80,9 @@ struct WaitableReplyContext {
|
|||
node_ref: NodeRef,
|
||||
send_ts: Timestamp,
|
||||
send_data_result: SendDataResult,
|
||||
safety_route: Option<BarePublicKey>,
|
||||
remote_private_route: Option<BarePublicKey>,
|
||||
reply_private_route: Option<BarePublicKey>,
|
||||
safety_route: Option<PublicKey>,
|
||||
remote_private_route: Option<PublicKey>,
|
||||
reply_private_route: Option<PublicKey>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -434,7 +434,7 @@ impl RPCProcessor {
|
|||
let routing_table = self.routing_table();
|
||||
let fanout_call = FanoutCall::new(
|
||||
&routing_table,
|
||||
node_id.into(),
|
||||
node_id.to_hash_coordinate(),
|
||||
count,
|
||||
fanout,
|
||||
0,
|
||||
|
@ -570,10 +570,10 @@ impl RPCProcessor {
|
|||
));
|
||||
}
|
||||
RPCMessageHeaderDetail::SafetyRouted(sr) => {
|
||||
let node_id = self
|
||||
let public_key = self
|
||||
.routing_table()
|
||||
.node_id(sr.direct.envelope.get_crypto_kind());
|
||||
if node_id.value() != reply_private_route.into() {
|
||||
.public_key(sr.direct.envelope.get_crypto_kind());
|
||||
if public_key != reply_private_route {
|
||||
return Err(RPCError::protocol(
|
||||
"should have received reply from safety route to a stub",
|
||||
));
|
||||
|
@ -600,7 +600,7 @@ impl RPCProcessor {
|
|||
routing_domain: RoutingDomain,
|
||||
safety_selection: SafetySelection,
|
||||
remote_private_route: PrivateRoute,
|
||||
reply_private_route: Option<BarePublicKey>,
|
||||
reply_private_route: Option<PublicKey>,
|
||||
message_data: Vec<u8>,
|
||||
) -> RPCNetworkResult<RenderedOperation> {
|
||||
let routing_table = self.routing_table();
|
||||
|
@ -609,7 +609,7 @@ impl RPCProcessor {
|
|||
|
||||
// Get useful private route properties
|
||||
let pr_is_stub = remote_private_route.is_stub();
|
||||
let pr_pubkey = remote_private_route.public_key.value();
|
||||
let pr_pubkey = remote_private_route.public_key.clone();
|
||||
let crypto_kind = remote_private_route.crypto_kind();
|
||||
let Some(vcrypto) = crypto.get(crypto_kind) else {
|
||||
return Err(RPCError::internal(
|
||||
|
@ -623,7 +623,7 @@ impl RPCProcessor {
|
|||
.compile_safety_route(safety_selection, remote_private_route)
|
||||
.to_rpc_network_result()?);
|
||||
let sr_is_stub = compiled_route.safety_route.is_stub();
|
||||
let sr_pubkey = compiled_route.safety_route.public_key.value();
|
||||
let sr_pubkey = compiled_route.safety_route.public_key.clone();
|
||||
|
||||
// Encrypt routed operation
|
||||
// Xmsg + ENC(Xmsg, DH(PKapr, SKbsr))
|
||||
|
@ -689,7 +689,7 @@ impl RPCProcessor {
|
|||
let reply_private_route = match operation.kind() {
|
||||
RPCOperationKind::Question(q) => match q.respond_to() {
|
||||
RespondTo::Sender => None,
|
||||
RespondTo::PrivateRoute(pr) => Some(pr.public_key.value()),
|
||||
RespondTo::PrivateRoute(pr) => Some(pr.public_key.clone()),
|
||||
},
|
||||
RPCOperationKind::Statement(_) | RPCOperationKind::Answer(_) => None,
|
||||
};
|
||||
|
@ -759,8 +759,8 @@ impl RPCProcessor {
|
|||
Some(pi) => pi,
|
||||
};
|
||||
let private_route = PrivateRoute::new_stub(
|
||||
match destination_node_ref.best_node_id() {
|
||||
Some(nid) => nid.into(),
|
||||
match destination_node_ref.best_public_key(routing_domain) {
|
||||
Some(pk) => pk,
|
||||
None => {
|
||||
return Ok(NetworkResult::no_connection_other(
|
||||
"No best node id for stub private route",
|
||||
|
@ -859,8 +859,8 @@ impl RPCProcessor {
|
|||
rpc_kind: RPCKind,
|
||||
send_ts: Timestamp,
|
||||
node_ref: NodeRef,
|
||||
safety_route: Option<BarePublicKey>,
|
||||
remote_private_route: Option<BarePublicKey>,
|
||||
safety_route: Option<PublicKey>,
|
||||
remote_private_route: Option<PublicKey>,
|
||||
) {
|
||||
let wants_answer = matches!(rpc_kind, RPCKind::Question);
|
||||
|
||||
|
@ -896,7 +896,7 @@ impl RPCProcessor {
|
|||
if context.safety_route.is_none() && context.remote_private_route.is_none() {
|
||||
context
|
||||
.node_ref
|
||||
.stats_lost_answer(context.send_data_result.is_ordered());
|
||||
.stats_lost_answer(context.send_data_result.sequence_ordering());
|
||||
|
||||
// Also clear the last_connections for the entry so we make a new connection next time
|
||||
context.node_ref.clear_last_flows();
|
||||
|
@ -936,9 +936,9 @@ impl RPCProcessor {
|
|||
send_ts: Timestamp,
|
||||
bytes: ByteCount,
|
||||
node_ref: NodeRef,
|
||||
safety_route: Option<BarePublicKey>,
|
||||
remote_private_route: Option<BarePublicKey>,
|
||||
ordered: bool,
|
||||
safety_route: Option<PublicKey>,
|
||||
remote_private_route: Option<PublicKey>,
|
||||
ordering: SequenceOrdering,
|
||||
) {
|
||||
// Record for node if this was not sent via a route
|
||||
if safety_route.is_none() && remote_private_route.is_none() {
|
||||
|
@ -948,7 +948,7 @@ impl RPCProcessor {
|
|||
if is_answer {
|
||||
node_ref.stats_answer_sent(bytes);
|
||||
} else {
|
||||
node_ref.stats_question_sent(send_ts, bytes, wants_answer, ordered);
|
||||
node_ref.stats_question_sent(send_ts, bytes, wants_answer, ordering);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -990,7 +990,7 @@ impl RPCProcessor {
|
|||
context.send_ts,
|
||||
recv_ts,
|
||||
bytes,
|
||||
context.send_data_result.is_ordered(),
|
||||
context.send_data_result.sequence_ordering(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -1193,7 +1193,7 @@ impl RPCProcessor {
|
|||
node_ref.unfiltered(),
|
||||
safety_route.clone(),
|
||||
remote_private_route.clone(),
|
||||
send_data_result.is_ordered(),
|
||||
send_data_result.sequence_ordering(),
|
||||
);
|
||||
|
||||
// Ref the connection so it doesn't go away until we're done with the waitable reply
|
||||
|
@ -1284,7 +1284,7 @@ impl RPCProcessor {
|
|||
node_ref.unfiltered(),
|
||||
safety_route,
|
||||
remote_private_route,
|
||||
send_data_result.is_ordered(),
|
||||
send_data_result.sequence_ordering(),
|
||||
);
|
||||
|
||||
Ok(NetworkResult::value(()))
|
||||
|
@ -1357,7 +1357,7 @@ impl RPCProcessor {
|
|||
node_ref.unfiltered(),
|
||||
safety_route,
|
||||
remote_private_route,
|
||||
send_data_result.is_ordered(),
|
||||
send_data_result.sequence_ordering(),
|
||||
);
|
||||
|
||||
Ok(NetworkResult::value(()))
|
||||
|
|
|
@ -9,11 +9,11 @@ pub struct RenderedOperation {
|
|||
/// Node to send envelope to (may not be destination node in case of relay)
|
||||
pub node_ref: FilteredNodeRef,
|
||||
/// The safety route used to send the message
|
||||
pub safety_route: Option<BarePublicKey>,
|
||||
pub safety_route: Option<PublicKey>,
|
||||
/// The private route used to send the message
|
||||
pub remote_private_route: Option<BarePublicKey>,
|
||||
pub remote_private_route: Option<PublicKey>,
|
||||
/// The private route requested to receive the reply
|
||||
pub reply_private_route: Option<BarePublicKey>,
|
||||
pub reply_private_route: Option<PublicKey>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for RenderedOperation {
|
||||
|
|
|
@ -120,7 +120,7 @@ impl RPCProcessor {
|
|||
|
||||
let closest_nodes = network_result_try!(routing_table.find_preferred_closest_peers(
|
||||
routing_domain,
|
||||
&node_id.clone().into(),
|
||||
node_id.to_hash_coordinate(),
|
||||
&capabilities
|
||||
));
|
||||
|
||||
|
|
|
@ -141,10 +141,9 @@ impl RPCProcessor {
|
|||
}
|
||||
|
||||
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
||||
let valid = match RoutingTable::verify_peers_closer(
|
||||
&vcrypto,
|
||||
&target_node_id.clone().into(),
|
||||
&record_key.clone().into(),
|
||||
let valid = match self.routing_table().verify_peers_closer(
|
||||
target_node_id.to_hash_coordinate(),
|
||||
record_key.to_hash_coordinate(),
|
||||
&peers,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
|
@ -226,7 +225,7 @@ impl RPCProcessor {
|
|||
let closer_to_key_peers = network_result_try!(routing_table
|
||||
.find_preferred_peers_closer_to_key(
|
||||
routing_domain,
|
||||
&record_key.clone().into(),
|
||||
record_key.to_hash_coordinate(),
|
||||
vec![CAP_DHT]
|
||||
));
|
||||
|
||||
|
|
|
@ -138,10 +138,9 @@ impl RPCProcessor {
|
|||
}
|
||||
|
||||
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
||||
let valid = match RoutingTable::verify_peers_closer(
|
||||
&vcrypto,
|
||||
&target_node_id.clone().into(),
|
||||
&record_key.clone().into(),
|
||||
let valid = match self.routing_table().verify_peers_closer(
|
||||
target_node_id.to_hash_coordinate(),
|
||||
record_key.to_hash_coordinate(),
|
||||
&peers,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
|
@ -214,7 +213,7 @@ impl RPCProcessor {
|
|||
let closer_to_key_peers = network_result_try!(routing_table
|
||||
.find_preferred_peers_closer_to_key(
|
||||
routing_domain,
|
||||
&record_key.clone().into(),
|
||||
record_key.to_hash_coordinate(),
|
||||
vec![CAP_DHT]
|
||||
));
|
||||
|
||||
|
|
|
@ -89,11 +89,8 @@ impl RPCProcessor {
|
|||
) -> RPCNetworkResult<()> {
|
||||
// Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret)
|
||||
// xxx: punish nodes that send messages that fail to decrypt eventually? How to do this for safety routes?
|
||||
let secret_key = self
|
||||
.routing_table()
|
||||
.secret_key(remote_sr_pubkey.kind())
|
||||
.value();
|
||||
let Ok(dh_secret) = vcrypto.cached_dh(remote_sr_pubkey.ref_value(), &secret_key) else {
|
||||
let secret_key = self.routing_table().secret_key(remote_sr_pubkey.kind());
|
||||
let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey, &secret_key) else {
|
||||
return Ok(NetworkResult::invalid_message(
|
||||
"dh failed for remote safety route for safety routed operation",
|
||||
));
|
||||
|
@ -116,7 +113,7 @@ impl RPCProcessor {
|
|||
// Pass message to RPC system
|
||||
self.enqueue_safety_routed_message(
|
||||
detail,
|
||||
remote_sr_pubkey.value(),
|
||||
remote_sr_pubkey,
|
||||
routed_operation.sequencing(),
|
||||
body,
|
||||
)
|
||||
|
@ -146,7 +143,7 @@ impl RPCProcessor {
|
|||
// Ensure the route is validated, and construct a return safetyspec that matches the inbound preferences
|
||||
let routing_table = self.routing_table();
|
||||
let rss = routing_table.route_spec_store();
|
||||
let preferred_route = rss.get_route_id_for_key(pr_pubkey.ref_value());
|
||||
let preferred_route = rss.get_route_id_for_key(&pr_pubkey);
|
||||
|
||||
let Some((secret_key, safety_spec)) = rss.with_signature_validated_route(
|
||||
&pr_pubkey,
|
||||
|
@ -172,7 +169,7 @@ impl RPCProcessor {
|
|||
|
||||
// Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret)
|
||||
// xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes?
|
||||
let Ok(dh_secret) = vcrypto.cached_dh(remote_sr_pubkey.ref_value(), &secret_key) else {
|
||||
let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey, &secret_key) else {
|
||||
return Ok(NetworkResult::invalid_message(
|
||||
"dh failed for remote safety route for private routed operation",
|
||||
));
|
||||
|
@ -189,14 +186,8 @@ impl RPCProcessor {
|
|||
};
|
||||
|
||||
// Pass message to RPC system
|
||||
self.enqueue_private_routed_message(
|
||||
detail,
|
||||
remote_sr_pubkey.value(),
|
||||
pr_pubkey.value(),
|
||||
safety_spec,
|
||||
body,
|
||||
)
|
||||
.map_err(RPCError::internal)?;
|
||||
self.enqueue_private_routed_message(detail, remote_sr_pubkey, pr_pubkey, safety_spec, body)
|
||||
.map_err(RPCError::internal)?;
|
||||
|
||||
Ok(NetworkResult::value(()))
|
||||
}
|
||||
|
@ -210,13 +201,9 @@ impl RPCProcessor {
|
|||
remote_sr_pubkey: PublicKey,
|
||||
pr_pubkey: PublicKey,
|
||||
) -> RPCNetworkResult<()> {
|
||||
// If the private route public key is our node id, then this was sent via safety route to our node directly
|
||||
// If the private route public key is our public key, then this was sent via safety route to our node directly
|
||||
// so there will be no signatures to validate
|
||||
if self
|
||||
.routing_table()
|
||||
.node_ids()
|
||||
.contains(&pr_pubkey.clone().into())
|
||||
{
|
||||
if self.routing_table().public_keys().contains(&pr_pubkey) {
|
||||
// The private route was a stub
|
||||
self.process_safety_routed_operation(
|
||||
detail,
|
||||
|
@ -310,9 +297,9 @@ impl RPCProcessor {
|
|||
};
|
||||
|
||||
// Decrypt the blob with DEC(nonce, DH(the PR's public key, this hop's secret)
|
||||
let secret_key = self.routing_table().secret_key(crypto_kind).value();
|
||||
let secret_key = self.routing_table().secret_key(crypto_kind);
|
||||
let dh_secret = vcrypto
|
||||
.cached_dh(pr_pubkey.ref_value(), &secret_key)
|
||||
.cached_dh(pr_pubkey, &secret_key)
|
||||
.map_err(RPCError::protocol)?;
|
||||
let dec_blob_data = match vcrypto.decrypt_aead(
|
||||
&route_hop_data.blob,
|
||||
|
@ -343,18 +330,11 @@ impl RPCProcessor {
|
|||
// Sign the operation if this is not our last hop
|
||||
// as the last hop is already signed by the envelope
|
||||
if route_hop.next_hop.is_some() {
|
||||
let node_id = self.routing_table().node_id(crypto_kind);
|
||||
let secret_key = self.routing_table().secret_key(crypto_kind).value();
|
||||
let sig = Signature::new(
|
||||
vcrypto.kind(),
|
||||
vcrypto
|
||||
.sign(
|
||||
&node_id.value().into(),
|
||||
&secret_key,
|
||||
routed_operation.data(),
|
||||
)
|
||||
.map_err(RPCError::internal)?,
|
||||
);
|
||||
let public_key = self.routing_table().public_key(crypto_kind);
|
||||
let secret_key = self.routing_table().secret_key(crypto_kind);
|
||||
let sig = vcrypto
|
||||
.sign(&public_key, &secret_key, routed_operation.data())
|
||||
.map_err(RPCError::internal)?;
|
||||
routed_operation.add_signature(sig);
|
||||
}
|
||||
|
||||
|
@ -413,10 +393,8 @@ impl RPCProcessor {
|
|||
// There is a safety route hop
|
||||
SafetyRouteHops::Data(ref route_hop_data) => {
|
||||
// Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret)
|
||||
let secret_key = self.routing_table().secret_key(crypto_kind).value();
|
||||
let Ok(dh_secret) =
|
||||
vcrypto.cached_dh(safety_route.public_key.ref_value(), &secret_key)
|
||||
else {
|
||||
let secret_key = self.routing_table().secret_key(crypto_kind);
|
||||
let Ok(dh_secret) = vcrypto.cached_dh(&safety_route.public_key, &secret_key) else {
|
||||
return Ok(NetworkResult::invalid_message(
|
||||
"dh failed for safety route hop",
|
||||
));
|
||||
|
|
|
@ -154,10 +154,9 @@ impl RPCProcessor {
|
|||
}
|
||||
|
||||
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
||||
let valid = match RoutingTable::verify_peers_closer(
|
||||
&vcrypto,
|
||||
&target_node_id.clone().into(),
|
||||
&record_key.clone().into(),
|
||||
let valid = match self.routing_table().verify_peers_closer(
|
||||
target_node_id.to_hash_coordinate(),
|
||||
record_key.to_hash_coordinate(),
|
||||
&peers,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
|
@ -241,7 +240,7 @@ impl RPCProcessor {
|
|||
let closer_to_key_peers = network_result_try!(routing_table
|
||||
.find_preferred_peers_closer_to_key(
|
||||
routing_domain,
|
||||
&record_key.clone().into(),
|
||||
record_key.to_hash_coordinate(),
|
||||
vec![CAP_DHT]
|
||||
));
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ impl_veilid_log_facility!("rpc");
|
|||
impl RPCProcessor {
|
||||
// Can only be sent directly, not via relays or routes
|
||||
#[instrument(level = "trace", target = "rpc", skip(self), ret, err(level=Level::DEBUG))]
|
||||
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), expect(dead_code))]
|
||||
pub async fn rpc_call_validate_dial_info(
|
||||
&self,
|
||||
peer: NodeRef,
|
||||
|
|
|
@ -62,10 +62,10 @@ impl RPCProcessor {
|
|||
"not processing value change over safety route",
|
||||
));
|
||||
}
|
||||
RPCMessageHeaderDetail::PrivateRouted(p) => NodeId::new(
|
||||
p.direct.envelope.get_crypto_kind(),
|
||||
p.remote_safety_route.clone().into(),
|
||||
),
|
||||
RPCMessageHeaderDetail::PrivateRouted(p) => self
|
||||
.routing_table()
|
||||
.generate_node_id(&p.remote_safety_route)
|
||||
.map_err(RPCError::internal)?,
|
||||
};
|
||||
|
||||
if debug_target_enabled!("dht") {
|
||||
|
|
|
@ -153,10 +153,9 @@ impl RPCProcessor {
|
|||
}
|
||||
|
||||
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
||||
let valid = match RoutingTable::verify_peers_closer(
|
||||
&vcrypto,
|
||||
&target_node_id.clone().into(),
|
||||
&record_key.clone().into(),
|
||||
let valid = match self.routing_table().verify_peers_closer(
|
||||
target_node_id.to_hash_coordinate(),
|
||||
record_key.to_hash_coordinate(),
|
||||
&peers,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
|
@ -266,7 +265,7 @@ impl RPCProcessor {
|
|||
let closer_to_key_peers = network_result_try!(routing_table
|
||||
.find_preferred_peers_closer_to_key(
|
||||
routing_domain,
|
||||
&record_key.clone().into(),
|
||||
record_key.to_hash_coordinate(),
|
||||
vec![CAP_DHT]
|
||||
));
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ impl RPCProcessor {
|
|||
pub(super) fn enqueue_safety_routed_message(
|
||||
&self,
|
||||
direct: RPCMessageHeaderDetailDirect,
|
||||
remote_safety_route: BarePublicKey,
|
||||
remote_safety_route: PublicKey,
|
||||
sequencing: Sequencing,
|
||||
body: Vec<u8>,
|
||||
) -> EyreResult<()> {
|
||||
|
@ -203,8 +203,8 @@ impl RPCProcessor {
|
|||
pub(super) fn enqueue_private_routed_message(
|
||||
&self,
|
||||
direct: RPCMessageHeaderDetailDirect,
|
||||
remote_safety_route: BarePublicKey,
|
||||
private_route: BarePublicKey,
|
||||
remote_safety_route: PublicKey,
|
||||
private_route: PublicKey,
|
||||
safety_spec: SafetySpec,
|
||||
body: Vec<u8>,
|
||||
) -> EyreResult<()> {
|
||||
|
|
|
@ -259,7 +259,7 @@ impl StorageManager {
|
|||
let routing_table = registry.routing_table();
|
||||
let fanout_call = FanoutCall::new(
|
||||
&routing_table,
|
||||
record_key.clone().into(),
|
||||
record_key.to_hash_coordinate(),
|
||||
key_count,
|
||||
fanout,
|
||||
consensus_count,
|
||||
|
@ -374,18 +374,11 @@ impl StorageManager {
|
|||
return Ok(None);
|
||||
};
|
||||
|
||||
// Get cryptosystem
|
||||
let crypto = self.crypto();
|
||||
let Some(vcrypto) = crypto.get(record_key.kind()) else {
|
||||
apibail_generic!("unsupported cryptosystem");
|
||||
};
|
||||
|
||||
// Keep the list of nodes that returned a value for later reference
|
||||
let mut inner = self.inner.lock().await;
|
||||
|
||||
Self::process_fanout_results_inner(
|
||||
&mut inner,
|
||||
&vcrypto,
|
||||
record_key.clone(),
|
||||
core::iter::once((ValueSubkeyRangeSet::single(subkey), result.fanout_result)),
|
||||
false,
|
||||
|
|
|
@ -294,7 +294,7 @@ impl StorageManager {
|
|||
let routing_table = self.routing_table();
|
||||
let fanout_call = FanoutCall::new(
|
||||
&routing_table,
|
||||
record_key.into(),
|
||||
record_key.to_hash_coordinate(),
|
||||
key_count,
|
||||
fanout,
|
||||
consensus_count,
|
||||
|
|
|
@ -198,7 +198,7 @@ impl StorageManager {
|
|||
for ck in VALID_CRYPTO_KINDS {
|
||||
let vcrypto = crypto.get(ck).unwrap();
|
||||
let kp = vcrypto.generate_keypair();
|
||||
anonymous_watch_keys.add(KeyPair::new(ck, kp));
|
||||
anonymous_watch_keys.add(kp);
|
||||
}
|
||||
|
||||
let inner = Self::new_inner();
|
||||
|
@ -530,11 +530,11 @@ impl StorageManager {
|
|||
schema_data: &[u8],
|
||||
) -> RecordKey {
|
||||
let mut hash_data = Vec::<u8>::with_capacity(owner_key.len() + 4 + schema_data.len());
|
||||
hash_data.extend_from_slice(&vcrypto.kind().0);
|
||||
hash_data.extend_from_slice(vcrypto.kind().bytes());
|
||||
hash_data.extend_from_slice(owner_key);
|
||||
hash_data.extend_from_slice(schema_data);
|
||||
let hash = vcrypto.generate_hash(&hash_data);
|
||||
RecordKey::new(vcrypto.kind(), BareRecordKey::from(hash))
|
||||
RecordKey::new(vcrypto.kind(), BareRecordKey::new(hash.ref_value()))
|
||||
}
|
||||
|
||||
/// Create a local record from scratch with a new owner key, open it, and return the opened descriptor
|
||||
|
@ -894,7 +894,7 @@ impl StorageManager {
|
|||
&descriptor.owner(),
|
||||
subkey,
|
||||
&vcrypto,
|
||||
&writer.bare_secret(),
|
||||
&writer.secret(),
|
||||
)?);
|
||||
|
||||
// Check if we are offline
|
||||
|
@ -1293,12 +1293,6 @@ impl StorageManager {
|
|||
subkeys
|
||||
};
|
||||
|
||||
// Get cryptosystem
|
||||
let crypto = self.crypto();
|
||||
let Some(vcrypto) = crypto.get(record_key.kind()) else {
|
||||
apibail_generic!("unsupported cryptosystem");
|
||||
};
|
||||
|
||||
let mut inner = self.inner.lock().await;
|
||||
let safety_selection = {
|
||||
let Some(opened_record) = inner.opened_records.get(&record_key) else {
|
||||
|
@ -1390,7 +1384,6 @@ impl StorageManager {
|
|||
|
||||
Self::process_fanout_results_inner(
|
||||
&mut inner,
|
||||
&vcrypto,
|
||||
record_key.clone(),
|
||||
results_iter,
|
||||
false,
|
||||
|
@ -1552,7 +1545,7 @@ impl StorageManager {
|
|||
}
|
||||
owner
|
||||
} else {
|
||||
KeyPair::new(vcrypto.kind(), vcrypto.generate_keypair())
|
||||
vcrypto.generate_keypair()
|
||||
};
|
||||
|
||||
// Calculate dht key
|
||||
|
@ -1563,7 +1556,7 @@ impl StorageManager {
|
|||
owner.key(),
|
||||
schema_data,
|
||||
&vcrypto,
|
||||
owner.bare_secret(),
|
||||
owner.secret(),
|
||||
)?);
|
||||
|
||||
// Add new local value record
|
||||
|
@ -1704,7 +1697,7 @@ impl StorageManager {
|
|||
// Otherwise this is just another subkey writer
|
||||
let owner_secret = if let Some(writer) = writer.clone() {
|
||||
if writer.key() == owner {
|
||||
Some(writer.bare_secret())
|
||||
Some(writer.secret())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1753,7 +1746,7 @@ impl StorageManager {
|
|||
// Otherwise this is just another subkey writer
|
||||
let owner_secret = if let Some(writer) = &writer {
|
||||
if writer.key() == owner {
|
||||
Some(writer.bare_secret())
|
||||
Some(writer.secret())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1807,12 +1800,7 @@ impl StorageManager {
|
|||
d.nodes
|
||||
.keys()
|
||||
.cloned()
|
||||
.filter_map(|x| {
|
||||
routing_table
|
||||
.lookup_node_ref(NodeId::new(record_key.kind(), x))
|
||||
.ok()
|
||||
.flatten()
|
||||
})
|
||||
.filter_map(|nr| routing_table.lookup_node_ref(nr).ok().flatten())
|
||||
.collect()
|
||||
});
|
||||
|
||||
|
@ -1822,7 +1810,6 @@ impl StorageManager {
|
|||
#[instrument(level = "trace", target = "stor", skip_all)]
|
||||
fn process_fanout_results_inner<I: IntoIterator<Item = (ValueSubkeyRangeSet, FanoutResult)>>(
|
||||
inner: &mut StorageManagerInner,
|
||||
vcrypto: &CryptoSystemGuard<'_>,
|
||||
record_key: RecordKey,
|
||||
subkey_results_iter: I,
|
||||
is_set: bool,
|
||||
|
@ -1839,7 +1826,7 @@ impl StorageManager {
|
|||
for node_id in fanout_result
|
||||
.value_nodes
|
||||
.iter()
|
||||
.filter_map(|x| x.node_ids().get(record_key.kind()).map(|k| k.value()))
|
||||
.filter_map(|x| x.node_ids().get(record_key.kind()))
|
||||
{
|
||||
let pnd = d.nodes.entry(node_id).or_default();
|
||||
if is_set || pnd.last_set == Timestamp::default() {
|
||||
|
@ -1863,14 +1850,13 @@ impl StorageManager {
|
|||
return res;
|
||||
}
|
||||
// Distance is the next metric, closer nodes first
|
||||
let da = vcrypto.distance(
|
||||
&BareHashDigest::from(a.0.clone()),
|
||||
&BareHashDigest::from(record_key.value()),
|
||||
);
|
||||
let db = vcrypto.distance(
|
||||
&BareHashDigest::from(b.0.clone()),
|
||||
&BareHashDigest::from(record_key.value()),
|
||||
);
|
||||
let da =
|
||||
a.0.to_hash_coordinate()
|
||||
.distance(&record_key.to_hash_coordinate());
|
||||
|
||||
let db =
|
||||
b.0.to_hash_coordinate()
|
||||
.distance(&record_key.to_hash_coordinate());
|
||||
da.cmp(&db)
|
||||
});
|
||||
|
||||
|
@ -1969,7 +1955,7 @@ impl StorageManager {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", target = "stor", skip_all, err)]
|
||||
pub(super) async fn handle_inspect_local_value_inner(
|
||||
async fn handle_inspect_local_value_inner(
|
||||
&self,
|
||||
inner: &mut StorageManagerInner,
|
||||
record_key: RecordKey,
|
||||
|
@ -1998,7 +1984,7 @@ impl StorageManager {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", target = "stor", skip_all, err)]
|
||||
pub(super) async fn handle_get_remote_value_inner(
|
||||
async fn handle_get_remote_value_inner(
|
||||
inner: &mut StorageManagerInner,
|
||||
record_key: RecordKey,
|
||||
subkey: ValueSubkey,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue