diff --git a/.vscode/launch.json b/.vscode/launch.json index f8f9a1d2..1156a811 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -42,16 +42,6 @@ ], "terminal": "console" }, - // { - // "type": "lldb", - // "request": "launch", - // "name": "Debug veilid-server", - // "cargo": { - // "args": ["run", "--manifest-path", "veilid-server/Cargo.toml"] - // }, - // "args": ["--trace"], - // "cwd": "${workspaceFolder}/veilid-server" - // } { "type": "lldb", "request": "launch", diff --git a/veilid-cli/src/client_api_connection.rs b/veilid-cli/src/client_api_connection.rs index 84687051..0bb89122 100644 --- a/veilid-cli/src/client_api_connection.rs +++ b/veilid-cli/src/client_api_connection.rs @@ -195,45 +195,55 @@ impl ClientApiConnection { let rpc_jh = spawn_local(rpc_system); - // Send the request and get the state object and the registration object - let response = request - .send() - .promise - .await - .map_err(|e| format!("failed to send register request: {}", e))?; - let response = response - .get() - .map_err(|e| format!("failed to get register response: {}", e))?; + let reg_res: Result = (async { + // Send the request and get the state object and the registration object + let response = request + .send() + .promise + .await + .map_err(|e| format!("failed to send register request: {}", e))?; + let response = response + .get() + .map_err(|e| format!("failed to get register response: {}", e))?; - // Get the registration object, which drops our connection when it is dropped - let _registration = response - .get_registration() - .map_err(|e| format!("failed to get registration object: {}", e))?; + // Get the registration object, which drops our connection when it is dropped + let registration = response + .get_registration() + .map_err(|e| format!("failed to get registration object: {}", e))?; - // Get the initial veilid state - let veilid_state = response - .get_state() - .map_err(|e| format!("failed to get initial veilid state: {}", e))?; + // Get the initial veilid state + let veilid_state = response + .get_state() + .map_err(|e| format!("failed to get initial veilid state: {}", e))?; - // Set up our state for the first time - let veilid_state: VeilidState = deserialize_json(veilid_state) - .map_err(|e| format!("failed to get deserialize veilid state: {}", e))?; - self.process_veilid_state(veilid_state).await?; + // Set up our state for the first time + let veilid_state: VeilidState = deserialize_json(veilid_state) + .map_err(|e| format!("failed to get deserialize veilid state: {}", e))?; + self.process_veilid_state(veilid_state).await?; - // Save server settings - let server_settings = response - .get_settings() - .map_err(|e| format!("failed to get initial veilid server settings: {}", e))? - .to_owned(); - self.inner.borrow_mut().server_settings = Some(server_settings.clone()); + // Save server settings + let server_settings = response + .get_settings() + .map_err(|e| format!("failed to get initial veilid server settings: {}", e))? + .to_owned(); + self.inner.borrow_mut().server_settings = Some(server_settings.clone()); - // Don't drop the registration, doing so will remove the client - // object mapping from the server which we need for the update backchannel + // Don't drop the registration, doing so will remove the client + // object mapping from the server which we need for the update backchannel + Ok(registration) + }) + .await; + + let _registration = match reg_res { + Ok(v) => v, + Err(e) => { + rpc_jh.abort().await; + return Err(e); + } + }; // Wait until rpc system completion or disconnect was requested let res = rpc_jh.await; - // #[cfg(feature = "rt-tokio")] - // let res = res.map_err(|e| format!("join error: {}", e))?; res.map_err(|e| format!("client RPC system error: {}", e)) } diff --git a/veilid-cli/src/ui.rs b/veilid-cli/src/ui.rs index 729a7a11..793e71cf 100644 --- a/veilid-cli/src/ui.rs +++ b/veilid-cli/src/ui.rs @@ -882,16 +882,10 @@ impl UI { pub fn set_config(&mut self, config: VeilidConfigInner) { let mut inner = self.inner.borrow_mut(); - let tkv: Vec = config - .network - .routing_table - .node_ids - .iter() - .filter_map(|(k, v)| v.node_id.map(|nid| TypedKey::new(*k, nid))) - .collect(); - let tks = TypedKeySet::from(tkv); - - inner.ui_state.node_id.set(tks.to_string()); + inner + .ui_state + .node_id + .set(config.network.routing_table.node_id.to_string()); } pub fn set_connection_state(&mut self, state: ConnectionState) { let mut inner = self.inner.borrow_mut(); diff --git a/veilid-core/src/crypto/crypto_system.rs b/veilid-core/src/crypto/crypto_system.rs index 12a50c38..5de8fb57 100644 --- a/veilid-core/src/crypto/crypto_system.rs +++ b/veilid-core/src/crypto/crypto_system.rs @@ -20,7 +20,7 @@ pub trait CryptoSystem { key: &PublicKey, secret: &SecretKey, ) -> Result; - fn generate_keypair(&self) -> (PublicKey, SecretKey); + fn generate_keypair(&self) -> KeyPair; fn generate_hash(&self, data: &[u8]) -> PublicKey; fn generate_hash_reader( &self, diff --git a/veilid-core/src/crypto/mod.rs b/veilid-core/src/crypto/mod.rs index 980f4dfc..dbe666f8 100644 --- a/veilid-core/src/crypto/mod.rs +++ b/veilid-core/src/crypto/mod.rs @@ -117,16 +117,15 @@ impl Crypto { let mut cache_validity_key: Vec = Vec::new(); { let c = self.unlocked_inner.config.get(); - for ck in &VALID_CRYPTO_KINDS { + for ck in VALID_CRYPTO_KINDS { cache_validity_key.append( &mut c .network .routing_table - .node_ids + .node_id .get(ck) .unwrap() - .node_id - .unwrap() + .value .bytes .to_vec(), ); @@ -223,14 +222,14 @@ impl Crypto { node_ids: &[TypedKey], data: &[u8], typed_signatures: &[TypedSignature], - ) -> Result, VeilidAPIError> { - let mut out = Vec::with_capacity(node_ids.len()); + ) -> Result { + let mut out = TypedKeySet::with_capacity(node_ids.len()); for sig in typed_signatures { for nid in node_ids { if nid.kind == sig.kind { if let Some(vcrypto) = self.get(sig.kind) { vcrypto.verify(&nid.value, data, &sig.value)?; - out.push(nid.kind); + out.add(*nid); } } } @@ -260,6 +259,16 @@ impl Crypto { Ok(out) } + /// Generate keypair + /// Does not require startup/init + pub fn generate_keypair(crypto_kind: CryptoKind) -> Result { + if crypto_kind == CRYPTO_KIND_VLD0 { + let kp = vld0_generate_keypair(); + return Ok(TypedKeyPair::new(crypto_kind, kp)); + } + Err(VeilidAPIError::generic("invalid crypto kind")) + } + // Internal utilities fn cached_dh_internal( diff --git a/veilid-core/src/crypto/tests/test_crypto.rs b/veilid-core/src/crypto/tests/test_crypto.rs index 8c5a524d..3f236b2e 100644 --- a/veilid-core/src/crypto/tests/test_crypto.rs +++ b/veilid-core/src/crypto/tests/test_crypto.rs @@ -138,8 +138,8 @@ pub async fn test_no_auth(vcrypto: CryptoSystemVersion) { pub async fn test_dh(vcrypto: CryptoSystemVersion) { trace!("test_dh"); - let (dht_key, dht_key_secret) = vcrypto.generate_keypair(); - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair(); + let (dht_key, dht_key_secret) = vcrypto.generate_keypair().into_split(); + let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split(); let r1 = vcrypto.compute_dh(&dht_key, &dht_key_secret2).unwrap(); let r2 = vcrypto.compute_dh(&dht_key2, &dht_key_secret).unwrap(); diff --git a/veilid-core/src/crypto/tests/test_envelope_receipt.rs b/veilid-core/src/crypto/tests/test_envelope_receipt.rs index f39a69bb..1e2698fc 100644 --- a/veilid-core/src/crypto/tests/test_envelope_receipt.rs +++ b/veilid-core/src/crypto/tests/test_envelope_receipt.rs @@ -9,8 +9,8 @@ pub async fn test_envelope_round_trip( // Create envelope let ts = Timestamp::from(0x12345678ABCDEF69u64); let nonce = vcrypto.random_nonce(); - let (sender_id, sender_secret) = vcrypto.generate_keypair(); - let (recipient_id, recipient_secret) = vcrypto.generate_keypair(); + let (sender_id, sender_secret) = vcrypto.generate_keypair().into_split(); + let (recipient_id, recipient_secret) = vcrypto.generate_keypair().into_split(); let envelope = Envelope::new( envelope_version, vcrypto.kind(), @@ -66,7 +66,7 @@ pub async fn test_receipt_round_trip( // Create receipt let nonce = vcrypto.random_nonce(); - let (sender_id, sender_secret) = vcrypto.generate_keypair(); + let (sender_id, sender_secret) = vcrypto.generate_keypair().into_split(); let receipt = Receipt::try_new(envelope_version, vcrypto.kind(), nonce, sender_id, body) .expect("should not fail"); diff --git a/veilid-core/src/crypto/tests/test_types.rs b/veilid-core/src/crypto/tests/test_types.rs index 00c5ded4..739d0e5f 100644 --- a/veilid-core/src/crypto/tests/test_types.rs +++ b/veilid-core/src/crypto/tests/test_types.rs @@ -10,8 +10,8 @@ static EMPTY_KEY_SECRET: [u8; SECRET_KEY_LENGTH] = [0u8; SECRET_KEY_LENGTH]; pub async fn test_generate_secret(vcrypto: CryptoSystemVersion) { // Verify keys generate - let (dht_key, dht_key_secret) = vcrypto.generate_keypair(); - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair(); + let (dht_key, dht_key_secret) = vcrypto.generate_keypair().into_split(); + let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split(); // Verify byte patterns are different between public and secret assert_ne!(dht_key.bytes, dht_key_secret.bytes); @@ -24,8 +24,8 @@ pub async fn test_generate_secret(vcrypto: CryptoSystemVersion) { pub async fn test_sign_and_verify(vcrypto: CryptoSystemVersion) { // Make two keys - let (dht_key, dht_key_secret) = vcrypto.generate_keypair(); - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair(); + let (dht_key, dht_key_secret) = vcrypto.generate_keypair().into_split(); + let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split(); // Sign the same message twice let dht_sig = vcrypto .sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes()) @@ -133,10 +133,10 @@ pub async fn test_key_conversions(vcrypto: CryptoSystemVersion) { assert_eq!(dht_key_secret_string, dht_key_string); // Make different keys - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair(); + let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split(); trace!("dht_key2: {:?}", dht_key2); trace!("dht_key_secret2: {:?}", dht_key_secret2); - let (dht_key3, _dht_key_secret3) = vcrypto.generate_keypair(); + let (dht_key3, _dht_key_secret3) = vcrypto.generate_keypair().into_split(); trace!("dht_key3: {:?}", dht_key3); trace!("_dht_key_secret3: {:?}", _dht_key_secret3); @@ -196,7 +196,7 @@ pub async fn test_encode_decode(vcrypto: CryptoSystemVersion) { assert_eq!(dht_key, dht_key_b); assert_eq!(dht_key_secret, dht_key_secret_b); - let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair(); + let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair().into_split(); let e1 = dht_key.encode(); trace!("e1: {:?}", e1); diff --git a/veilid-core/src/crypto/types/keypair.rs b/veilid-core/src/crypto/types/keypair.rs index 514afb06..253f84ea 100644 --- a/veilid-core/src/crypto/types/keypair.rs +++ b/veilid-core/src/crypto/types/keypair.rs @@ -24,6 +24,12 @@ impl KeyPair { pub fn new(key: PublicKey, secret: SecretKey) -> Self { Self { key, secret } } + pub fn split(&self) -> (PublicKey, SecretKey) { + (self.key, self.secret) + } + pub fn into_split(self) -> (PublicKey, SecretKey) { + (self.key, self.secret) + } } impl Encodable for KeyPair { diff --git a/veilid-core/src/crypto/vld0/mod.rs b/veilid-core/src/crypto/vld0/mod.rs index 58eb1d49..fd2b42a4 100644 --- a/veilid-core/src/crypto/vld0/mod.rs +++ b/veilid-core/src/crypto/vld0/mod.rs @@ -32,13 +32,13 @@ fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result (PublicKey, SecretKey) { +pub fn vld0_generate_keypair() -> KeyPair { let mut csprng = VeilidRng {}; let keypair = ed::Keypair::generate(&mut csprng); let dht_key = PublicKey::new(keypair.public.to_bytes()); let dht_key_secret = SecretKey::new(keypair.secret.to_bytes()); - (dht_key, dht_key_secret) + KeyPair::new(dht_key, dht_key_secret) } /// V0 CryptoSystem @@ -95,7 +95,7 @@ impl CryptoSystem for CryptoSystemVLD0 { let sk_xd = ed25519_to_x25519_sk(&sk_ed)?; Ok(SharedSecret::new(sk_xd.diffie_hellman(&pk_xd).to_bytes())) } - fn generate_keypair(&self) -> (PublicKey, SecretKey) { + fn generate_keypair(&self) -> KeyPair { vld0_generate_keypair() } fn generate_hash(&self, data: &[u8]) -> PublicKey { diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index e9fd9aee..5884c69a 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -527,7 +527,7 @@ impl NetworkManager { let nonce = vcrypto.random_nonce(); let node_id = routing_table.node_id(vcrypto.kind()); - let node_id_secret = routing_table.node_id_secret(vcrypto.kind()); + let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; let out = receipt @@ -556,7 +556,7 @@ impl NetworkManager { let nonce = vcrypto.random_nonce(); let node_id = routing_table.node_id(vcrypto.kind()); - let node_id_secret = routing_table.node_id_secret(vcrypto.kind()); + let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; let out = receipt @@ -754,7 +754,7 @@ impl NetworkManager { }; let node_id = routing_table.node_id(vcrypto.kind()); - let node_id_secret = routing_table.node_id_secret(vcrypto.kind()); + let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); // Get timestamp, nonce let ts = get_aligned_timestamp(); @@ -1427,7 +1427,7 @@ impl NetworkManager { } // DH to get decryption key (cached) - let node_id_secret = routing_table.node_id_secret(envelope.get_crypto_kind()); + let node_id_secret = routing_table.node_id_secret_key(envelope.get_crypto_kind()); // Decrypt the envelope body let body = match envelope diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index b9f5b976..746444c5 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -72,8 +72,10 @@ pub struct RoutingTableUnlockedInner { config: VeilidConfig, network_manager: NetworkManager, - /// The current node's public DHT keys and secrets - node_id_keypairs: BTreeMap, + /// The current node's public DHT keys + node_id: TypedKeySet, + /// The current node's public DHT secrets + node_id_secret: TypedSecretSet, /// Buckets to kick on our next kick task kick_queue: Mutex>, /// Background process for computing statistics @@ -113,33 +115,32 @@ impl RoutingTableUnlockedInner { } pub fn node_id(&self, kind: CryptoKind) -> TypedKey { - TypedKey::new(kind, self.node_id_keypairs.get(&kind).unwrap().key) + self.node_id.get(kind).unwrap() + } + + pub fn node_id_secret_key(&self, kind: CryptoKind) -> SecretKey { + self.node_id_secret.get(kind).unwrap().value } pub fn node_ids(&self) -> TypedKeySet { - let mut tks = TypedKeySet::new(); - for x in &self.node_id_keypairs { - tks.add(TypedKey::new(*x.0, x.1.key)); - } - tks + self.node_id.clone() } pub fn node_id_typed_key_pairs(&self) -> Vec { let mut tkps = Vec::new(); - for (ck, v) in &self.node_id_keypairs { - tkps.push(TypedKeyPair::new(*ck, *v)); + for ck in VALID_CRYPTO_KINDS { + tkps.push(TypedKeyPair::new( + ck, + KeyPair::new(self.node_id(ck).value, self.node_id_secret_key(ck)), + )); } tkps } - pub fn node_id_secret(&self, kind: CryptoKind) -> SecretKey { - self.node_id_keypairs.get(&kind).unwrap().secret - } - pub fn matches_own_node_id(&self, node_ids: &[TypedKey]) -> bool { for ni in node_ids { - if let Some(v) = self.node_id_keypairs.get(&ni.kind) { - if v.key == ni.value { + if let Some(v) = self.node_id.get(ni.kind) { + if v.value == ni.value { return true; } } @@ -148,8 +149,8 @@ impl RoutingTableUnlockedInner { } pub fn matches_own_node_id_key(&self, node_id_key: &PublicKey) -> bool { - for (_ck, v) in &self.node_id_keypairs { - if v.key == *node_id_key { + for tk in self.node_id.iter() { + if tk.value == *node_id_key { return true; } } @@ -158,12 +159,12 @@ impl RoutingTableUnlockedInner { pub fn calculate_bucket_index(&self, node_id: &TypedKey) -> BucketIndex { let crypto = self.crypto(); - let self_node_id = self.node_id_keypairs.get(&node_id.kind).unwrap().key; + let self_node_id_key = self.node_id(node_id.kind).value; let vcrypto = crypto.get(node_id.kind).unwrap(); ( node_id.kind, vcrypto - .distance(&node_id.value, &self_node_id) + .distance(&node_id.value, &self_node_id_key) .first_nonzero_bit() .unwrap(), ) @@ -182,22 +183,12 @@ impl RoutingTable { network_manager: NetworkManager, ) -> RoutingTableUnlockedInner { let c = config.get(); + RoutingTableUnlockedInner { config: config.clone(), network_manager, - node_id_keypairs: c - .network - .routing_table - .node_ids - .iter() - .map(|(k, v)| { - ( - *k, - KeyPair::new(v.node_id.unwrap(), v.node_id_secret.unwrap()), - ) - }) - .collect(), - + node_id: c.network.routing_table.node_id.clone(), + node_id_secret: c.network.routing_table.node_id_secret.clone(), kick_queue: Mutex::new(BTreeSet::default()), rolling_transfers_task: TickTask::new(ROLLING_TRANSFERS_INTERVAL_SECS), kick_buckets_task: TickTask::new(1), diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs index 1e26c545..913a5b17 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs @@ -490,12 +490,12 @@ impl RouteSpecStore { let mut route_set = BTreeMap::::new(); for crypto_kind in crypto_kinds.iter().copied() { let vcrypto = self.unlocked_inner.routing_table.crypto().get(crypto_kind).unwrap(); - let (public_key, secret_key) = vcrypto.generate_keypair(); + let keypair = vcrypto.generate_keypair(); let hops: Vec = route_nodes.iter().map(|v| nodes[*v].node_ids().get(crypto_kind).unwrap().value).collect(); - route_set.insert(public_key, RouteSpecDetail { + route_set.insert(keypair.key, RouteSpecDetail { crypto_kind, - secret_key, + secret_key: keypair.secret, hops, }); } @@ -888,7 +888,7 @@ impl RouteSpecStore { //println!("compile_safety_route profile (stub): {} us", (get_timestamp() - profile_start_ts)); return Ok(Some(CompiledRoute { safety_route: SafetyRoute::new_stub(routing_table.node_id(crypto_kind), private_route), - secret: routing_table.node_id_secret(crypto_kind), + secret: routing_table.node_id_secret_key(crypto_kind), first_hop, })); } diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index 7a944d82..0b8916d6 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -109,7 +109,7 @@ impl RPCProcessor { ) -> Result, RPCError> { // 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 node_id_secret = self.routing_table.node_id_secret(remote_sr_pubkey.kind); + let node_id_secret = self.routing_table.node_id_secret_key(remote_sr_pubkey.kind); let dh_secret = vcrypto .cached_dh(&remote_sr_pubkey.value, &node_id_secret) .map_err(RPCError::protocol)?; @@ -313,7 +313,7 @@ impl RPCProcessor { }; // Decrypt the blob with DEC(nonce, DH(the PR's public key, this hop's secret) - let node_id_secret = self.routing_table.node_id_secret(crypto_kind); + let node_id_secret = self.routing_table.node_id_secret_key(crypto_kind); let dh_secret = vcrypto .cached_dh(&pr_pubkey.value, &node_id_secret) .map_err(RPCError::protocol)?; @@ -345,7 +345,7 @@ impl RPCProcessor { // 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 node_id_secret = self.routing_table.node_id_secret(crypto_kind); + let node_id_secret = self.routing_table.node_id_secret_key(crypto_kind); let sig = vcrypto .sign(&node_id.value, &node_id_secret, &route_operation.data) .map_err(RPCError::internal)?; @@ -392,7 +392,7 @@ 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 node_id_secret = self.routing_table.node_id_secret(crypto_kind); + let node_id_secret = self.routing_table.node_id_secret_key(crypto_kind); let dh_secret = vcrypto .cached_dh(&route.safety_route.public_key.value, &node_id_secret) .map_err(RPCError::protocol)?; diff --git a/veilid-core/src/tests/common/test_table_store.rs b/veilid-core/src/tests/common/test_table_store.rs index 42a8694c..d7af7094 100644 --- a/veilid-core/src/tests/common/test_table_store.rs +++ b/veilid-core/src/tests/common/test_table_store.rs @@ -128,19 +128,19 @@ pub async fn test_frozen(vcrypto: CryptoSystemVersion, ts: TableStore) { let _ = ts.delete("test"); let db = ts.open("test", 3).await.expect("should have opened"); - let (dht_key, _) = vcrypto.generate_keypair(); + let keypair = vcrypto.generate_keypair(); - assert!(db.store_rkyv(0, b"asdf", &dht_key).await.is_ok()); + assert!(db.store_rkyv(0, b"asdf", &keypair).await.is_ok()); - assert_eq!(db.load_rkyv::(0, b"qwer").unwrap(), None); + assert_eq!(db.load_rkyv::(0, b"qwer").unwrap(), None); - let d = match db.load_rkyv::(0, b"asdf") { + let d = match db.load_rkyv::(0, b"asdf") { Ok(x) => x, Err(e) => { panic!("couldn't decode: {}", e); } }; - assert_eq!(d, Some(dht_key), "keys should be equal"); + assert_eq!(d, Some(keypair), "keys should be equal"); assert!( db.store(1, b"foo", b"1234567890").await.is_ok(), diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index b7a511a9..9347a7a2 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -320,7 +320,8 @@ pub async fn test_config() { assert_eq!(inner.network.rpc.timeout_ms, 10_000u32); assert_eq!(inner.network.rpc.max_route_hop_count, 4u8); assert_eq!(inner.network.rpc.default_route_hop_count, 1u8); - assert_eq!(inner.network.routing_table.node_ids.len(), 0); + assert_eq!(inner.network.routing_table.node_id.len(), 0); + assert_eq!(inner.network.routing_table.node_id_secret.len(), 0); assert_eq!(inner.network.routing_table.bootstrap, Vec::::new()); assert_eq!(inner.network.routing_table.limit_over_attached, 64u32); assert_eq!(inner.network.routing_table.limit_fully_attached, 32u32); diff --git a/veilid-core/src/tests/common/test_veilid_core.rs b/veilid-core/src/tests/common/test_veilid_core.rs index 38fd7890..eab1dde3 100644 --- a/veilid-core/src/tests/common/test_veilid_core.rs +++ b/veilid-core/src/tests/common/test_veilid_core.rs @@ -67,15 +67,15 @@ pub async fn test_signed_node_info() { }], }; - let (pkey, skey) = vcrypto.generate_keypair(); - + // Test correct validation + let keypair = vcrypto.generate_keypair(); let sni = SignedDirectNodeInfo::make_signatures( crypto.clone(), - vec![TypedKeyPair::new(ck, KeyPair::new(pkey, skey))], + vec![TypedKeyPair::new(ck, keypair)], node_info.clone(), ) .unwrap(); - let mut tks: TypedKeySet = TypedKey::new(ck, pkey).into(); + let mut tks: TypedKeySet = TypedKey::new(ck, keypair.key).into(); let oldtkslen = tks.len(); let _ = SignedDirectNodeInfo::new( crypto.clone(), @@ -88,6 +88,38 @@ pub async fn test_signed_node_info() { assert_eq!(tks.len(), oldtkslen); assert_eq!(tks.len(), sni.signatures.len()); + // Test incorrect validation + let keypair1 = vcrypto.generate_keypair(); + let mut tks1: TypedKeySet = TypedKey::new(ck, keypair1.key).into(); + let oldtks1len = tks1.len(); + let _ = SignedDirectNodeInfo::new( + crypto.clone(), + &mut tks1, + node_info.clone(), + sni.timestamp, + sni.signatures.clone(), + ) + .unwrap_err(); + assert_eq!(tks1.len(), oldtks1len); + assert_eq!(tks1.len(), sni.signatures.len()); + + // Test unsupported cryptosystem validation + let fake_crypto_kind: CryptoKind = FourCC::from([0, 1, 2, 3]); + let mut tksfake: TypedKeySet = TypedKey::new(fake_crypto_kind, PublicKey::default()).into(); + let mut sigsfake = sni.signatures.clone(); + sigsfake.push(TypedSignature::new(fake_crypto_kind, Signature::default())); + tksfake.add(TypedKey::new(ck, keypair.key)); + let sdnifake = SignedDirectNodeInfo::new( + crypto.clone(), + &mut tksfake, + node_info.clone(), + sni.timestamp, + sigsfake.clone(), + ) + .unwrap(); + assert_eq!(tksfake.len(), 1); + assert_eq!(sdnifake.signatures.len(), sigsfake.len()); + // Test relayed let node_info2 = NodeInfo { network_class: NetworkClass::OutboundOnly, @@ -101,13 +133,14 @@ pub async fn test_signed_node_info() { }], }; - let (pkey2, skey2) = vcrypto.generate_keypair(); - let mut tks2: TypedKeySet = TypedKey::new(ck, pkey2).into(); + // Test correct validation + let keypair2 = vcrypto.generate_keypair(); + let mut tks2: TypedKeySet = TypedKey::new(ck, keypair2.key).into(); let oldtks2len = tks2.len(); let sni2 = SignedRelayedNodeInfo::make_signatures( crypto.clone(), - vec![TypedKeyPair::new(ck, KeyPair::new(pkey2, skey2))], + vec![TypedKeyPair::new(ck, keypair2)], node_info2.clone(), tks.clone(), sni.clone(), @@ -116,9 +149,9 @@ pub async fn test_signed_node_info() { let _ = SignedRelayedNodeInfo::new( crypto.clone(), &mut tks2, - node_info2, - tks, - sni, + node_info2.clone(), + tks.clone(), + sni.clone(), sni2.timestamp, sni2.signatures.clone(), ) @@ -126,6 +159,45 @@ pub async fn test_signed_node_info() { assert_eq!(tks2.len(), oldtks2len); assert_eq!(tks2.len(), sni2.signatures.len()); + + // Test incorrect validation + let keypair3 = vcrypto.generate_keypair(); + let mut tks3: TypedKeySet = TypedKey::new(ck, keypair3.key).into(); + let oldtks3len = tks3.len(); + + let _ = SignedRelayedNodeInfo::new( + crypto.clone(), + &mut tks3, + node_info2.clone(), + tks.clone(), + sni.clone(), + sni2.timestamp, + sni2.signatures.clone(), + ) + .unwrap_err(); + + assert_eq!(tks3.len(), oldtks3len); + assert_eq!(tks3.len(), sni2.signatures.len()); + + // Test unsupported cryptosystem validation + let fake_crypto_kind: CryptoKind = FourCC::from([0, 1, 2, 3]); + let mut tksfake3: TypedKeySet = + TypedKey::new(fake_crypto_kind, PublicKey::default()).into(); + let mut sigsfake3 = sni2.signatures.clone(); + sigsfake3.push(TypedSignature::new(fake_crypto_kind, Signature::default())); + tksfake3.add(TypedKey::new(ck, keypair2.key)); + let srnifake = SignedRelayedNodeInfo::new( + crypto.clone(), + &mut tksfake3, + node_info2.clone(), + tks.clone(), + sni.clone(), + sni2.timestamp, + sigsfake3.clone(), + ) + .unwrap(); + assert_eq!(tksfake3.len(), 1); + assert_eq!(srnifake.signatures.len(), sigsfake3.len()); } api.shutdown().await; diff --git a/veilid-core/src/veilid_api/types.rs b/veilid-core/src/veilid_api/types.rs index 18c0e8d0..034e8aac 100644 --- a/veilid-core/src/veilid_api/types.rs +++ b/veilid-core/src/veilid_api/types.rs @@ -1869,7 +1869,8 @@ pub struct SignedDirectNodeInfo { pub signatures: Vec, } impl SignedDirectNodeInfo { - /// Returns a new SignedDirectNodeInfo that has its signatures validated. Will modify the node_ids set to only include node_ids whose signatures validate + /// Returns a new SignedDirectNodeInfo that has its signatures validated. + /// On success, this will modify the node_ids set to only include node_ids whose signatures validate. /// All signatures are stored however, as this can be passed to other nodes that may be able to validate those signatures. pub fn new( crypto: Crypto, @@ -1881,9 +1882,9 @@ impl SignedDirectNodeInfo { let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?; // Verify the signatures that we can - let valid_crypto_kinds = + let validated_node_ids = crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?; - xx wrong! should remove only the kinds that are not valid! also fix relayed node_ids.remove_all(&valid_crypto_kinds); + *node_ids = validated_node_ids; if node_ids.len() == 0 { apibail_generic!("no valid node ids in direct node info"); } @@ -1956,7 +1957,8 @@ pub struct SignedRelayedNodeInfo { } impl SignedRelayedNodeInfo { - /// Returns a new SignedRelayedNodeInfo that has its signatures validated. Will modify the node_ids set to only include node_ids whose signatures validate + /// Returns a new SignedRelayedNodeInfo that has its signatures validated. + /// On success, this will modify the node_ids set to only include node_ids whose signatures validate. /// All signatures are stored however, as this can be passed to other nodes that may be able to validate those signatures. pub fn new( crypto: Crypto, @@ -1969,10 +1971,9 @@ impl SignedRelayedNodeInfo { ) -> Result { let node_info_bytes = Self::make_signature_bytes(&node_info, &relay_ids, &relay_info, timestamp)?; - let valid_crypto_kinds = + let validated_node_ids = crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?; - - node_ids.remove_all(&valid_crypto_kinds); + *node_ids = validated_node_ids; if node_ids.len() == 0 { apibail_generic!("no valid node ids in relayed node info"); } diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index 1bfa1868..b4e9aed5 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -316,25 +316,6 @@ pub struct VeilidConfigRPC { pub default_route_hop_count: u8, } -/// Configure the per-crypto version configuration -/// -#[derive( - Default, - Debug, - Clone, - PartialEq, - Eq, - Serialize, - Deserialize, - RkyvArchive, - RkyvSerialize, - RkyvDeserialize, -)] -pub struct VeilidConfigNodeId { - pub node_id: Option, - pub node_id_secret: Option, -} - /// Configure the network routing table /// #[derive( @@ -350,7 +331,8 @@ pub struct VeilidConfigNodeId { RkyvDeserialize, )] pub struct VeilidConfigRoutingTable { - pub node_ids: BTreeMap, + pub node_id: TypedKeySet, + pub node_id_secret: TypedSecretSet, pub bootstrap: Vec, pub limit_over_attached: u32, pub limit_fully_attached: u32, @@ -621,58 +603,15 @@ impl VeilidConfig { let keyname = &stringify!($key)[6..]; let v = cb(keyname.to_owned())?; $key = match v.downcast() { - Ok(v) => *v, - Err(e) => { - apibail_generic!(format!("incorrect type for key {}: {:?}", keyname, type_name_of_val(&*e))) - } - }; - }; - } - // More complicated optional fields for node ids - macro_rules! get_config_node_ids { - () => { - let keys = cb("network.routing_table.node_id".to_owned())?; - let keys: TypedKeySet = match keys.downcast() { Ok(v) => *v, Err(e) => { apibail_generic!(format!( - "incorrect type for key 'network.routing_table.node_id': {:?}", + "incorrect type for key {}: {:?}", + keyname, type_name_of_val(&*e) )) } }; - - let secrets = cb("network.routing_table.node_id_secret".to_owned())?; - let secrets: TypedSecretSet = match secrets.downcast() { - Ok(v) => *v, - Err(e) => { - apibail_generic!(format!( - "incorrect type for key 'network.routing_table.node_id_secret': {:?}", - type_name_of_val(&*e) - )) - } - }; - - for ck in VALID_CRYPTO_KINDS { - if let Some(key) = keys.get(ck) { - inner - .network - .routing_table - .node_ids - .entry(ck) - .or_default() - .node_id = Some(key.value); - } - if let Some(secret) = secrets.get(ck) { - inner - .network - .routing_table - .node_ids - .entry(ck) - .or_default() - .node_id_secret = Some(secret.value); - } - } }; } @@ -701,7 +640,9 @@ impl VeilidConfig { get_config!(inner.network.max_connection_frequency_per_min); get_config!(inner.network.client_whitelist_timeout_ms); get_config!(inner.network.reverse_connection_receipt_time_ms); - get_config_node_ids!(); + get_config!(inner.network.hole_punch_receipt_time_ms); + get_config!(inner.network.routing_table.node_id); + get_config!(inner.network.routing_table.node_id_secret); get_config!(inner.network.routing_table.bootstrap); get_config!(inner.network.routing_table.limit_over_attached); get_config!(inner.network.routing_table.limit_fully_attached); @@ -778,11 +719,20 @@ impl VeilidConfig { self.inner.read() } + fn safe_config(&self) -> VeilidConfigInner { + let mut safe_cfg = self.inner.read().clone(); + + // Remove secrets + safe_cfg.network.routing_table.node_id_secret = TypedSecretSet::new(); + + safe_cfg + } + pub fn with_mut(&self, f: F) -> Result where F: FnOnce(&mut VeilidConfigInner) -> Result, { - let (out, config) = { + let out = { let inner = &mut *self.inner.write(); // Edit a copy let mut editedinner = inner.clone(); @@ -792,12 +742,13 @@ impl VeilidConfig { Self::validate(&mut editedinner)?; // Commit changes *inner = editedinner.clone(); - (out, editedinner) + out }; // Send configuration update to clients if let Some(update_cb) = &self.update_cb { - update_cb(VeilidUpdate::Config(VeilidStateConfig { config })); + let safe_cfg = self.safe_config(); + update_cb(VeilidUpdate::Config(VeilidStateConfig { config: safe_cfg })); } Ok(out) @@ -975,29 +926,23 @@ impl VeilidConfig { crypto: Crypto, protected_store: intf::ProtectedStore, ) -> Result<(), VeilidAPIError> { + let mut out_node_id = TypedKeySet::new(); + let mut out_node_id_secret = TypedSecretSet::new(); + for ck in VALID_CRYPTO_KINDS { let vcrypto = crypto .get(ck) .expect("Valid crypto kind is not actually valid."); - let mut node_id = self - .inner - .read() - .network - .routing_table - .node_ids - .get(&ck) - .map(|n| n.node_id) - .flatten(); + let mut node_id = self.inner.read().network.routing_table.node_id.get(ck); let mut node_id_secret = self .inner .read() .network .routing_table - .node_ids - .get(&ck) - .map(|n| n.node_id_secret) - .flatten(); + .node_id_secret + .get(ck); + // See if node id was previously stored in the protected store if node_id.is_none() { debug!("pulling node_id_{} from storage", ck); @@ -1007,8 +952,13 @@ impl VeilidConfig { .map_err(VeilidAPIError::internal)? { debug!("node_id_{} found in storage", ck); - node_id = - Some(PublicKey::try_decode(s.as_str()).map_err(VeilidAPIError::internal)?); + node_id = match TypedKey::from_str(s.as_str()) { + Ok(v) => Some(v), + Err(_) => { + debug!("node id in protected store is not valid"); + None + } + } } else { debug!("node_id_{} not found in storage", ck); } @@ -1023,8 +973,13 @@ impl VeilidConfig { .map_err(VeilidAPIError::internal)? { debug!("node_id_secret_{} found in storage", ck); - node_id_secret = - Some(SecretKey::try_decode(s.as_str()).map_err(VeilidAPIError::internal)?); + node_id_secret = match TypedSecret::from_str(s.as_str()) { + Ok(v) => Some(v), + Err(_) => { + debug!("node id secret in protected store is not valid"); + None + } + } } else { debug!("node_id_secret_{} not found in storage", ck); } @@ -1034,7 +989,7 @@ impl VeilidConfig { let (node_id, node_id_secret) = if let (Some(node_id), Some(node_id_secret)) = (node_id, node_id_secret) { // Validate node id - if !vcrypto.validate_keypair(&node_id, &node_id_secret) { + if !vcrypto.validate_keypair(&node_id.value, &node_id_secret.value) { apibail_generic!(format!( "node_id_secret_{} and node_id_key_{} don't match", ck, ck @@ -1044,30 +999,36 @@ impl VeilidConfig { } else { // If we still don't have a valid node id, generate one debug!("generating new node_id_{}", ck); - vcrypto.generate_keypair() + let kp = vcrypto.generate_keypair(); + (TypedKey::new(ck, kp.key), TypedSecret::new(ck, kp.secret)) }; - info!("Node Id ({}) is {}", ck, node_id.encode()); + info!("Node Id: {}", node_id); // Save the node id / secret in storage protected_store - .save_user_secret_string(format!("node_id_{}", ck), node_id.encode().as_str()) + .save_user_secret_string(format!("node_id_{}", ck), node_id.to_string()) .await .map_err(VeilidAPIError::internal)?; protected_store .save_user_secret_string( format!("node_id_secret_{}", ck), - node_id_secret.encode().as_str(), + node_id_secret.to_string(), ) .await .map_err(VeilidAPIError::internal)?; - self.with_mut(|c| { - let n = c.network.routing_table.node_ids.entry(ck).or_default(); - n.node_id = Some(node_id); - n.node_id_secret = Some(node_id_secret); - Ok(()) - })?; + // Save for config + out_node_id.add(node_id); + out_node_id_secret.add(node_id_secret); } + + // Commit back to config + self.with_mut(|c| { + c.network.routing_table.node_id = out_node_id; + c.network.routing_table.node_id_secret = out_node_id_secret; + Ok(()) + })?; + trace!("init_node_ids complete"); Ok(()) diff --git a/veilid-server/src/main.rs b/veilid-server/src/main.rs index ce2c4e24..19b08b71 100644 --- a/veilid-server/src/main.rs +++ b/veilid-server/src/main.rs @@ -18,9 +18,9 @@ use cfg_if::*; #[allow(unused_imports)] use color_eyre::eyre::{bail, ensure, eyre, Result as EyreResult, WrapErr}; use server::*; +use std::str::FromStr; use tools::*; use tracing::*; -use veilid_core::Encodable as _; use veilid_logs::*; #[allow(clippy::all)] @@ -44,9 +44,15 @@ fn main() -> EyreResult<()> { // --- Generate DHT Key --- if matches.occurrences_of("generate-key-pair") != 0 { - let (key, secret) = veilid_core::vld0_generate_keypair(); - println!("Public: {}\nSecret: {}", key.encode(), secret.encode()); - return Ok(()); + if let Some(ckstr) = matches.get_one::("generate-key-pair") { + let ck: veilid_core::CryptoKind = + veilid_core::FourCC::from_str(ckstr).wrap_err("couldn't parse crypto kind")?; + let tkp = veilid_core::Crypto::generate_keypair(ck).wrap_err("invalid crypto kind")?; + println!("{}", tkp.to_string()); + return Ok(()); + } else { + bail!("missing crypto kind"); + } } // See if we're just running a quick command diff --git a/veilid-wasm/tests/web.rs b/veilid-wasm/tests/web.rs index a986d126..2ae8ca70 100644 --- a/veilid-wasm/tests/web.rs +++ b/veilid-wasm/tests/web.rs @@ -37,9 +37,14 @@ fn init_callbacks() { case "capabilities.protocol_connect_wss": return true; case "capabilities.protocol_accept_wss": return false; case "tablestore.directory": return ""; - case "network.node_id": return "ZLd4uMYdP4qYLtxF6GqrzBb32Z6T3rE2FWMkWup1pdY"; - case "network.node_id_secret": return "s2Gvq6HJOxgQh-3xIgfWSL3I-DWZ2c1RjZLJl2Xmg2E"; - case "network.bootstrap": return []; + case "network.routing_table.node_id": return []; + case "network.routing_table.node_id_secret": return []; + case "network.routing_table.bootstrap": return []; + case "network.routing_table.limit_over_attached": return 64; + case "network.routing_table.limit_fully_attached": return 32; + case "network.routing_table.limit_attached_strong": return 16; + case "network.routing_table.limit_attached_good": return 8; + case "network.routing_table.limit_attached_weak": return 4; case "network.rpc.concurrency": return 2; case "network.rpc.queue_size": return 128; case "network.rpc.max_timestamp_behind": return 10000000;