private route work

This commit is contained in:
John Smith
2022-11-01 21:05:48 -04:00
parent 5ae0bd834c
commit d96b83fb4e
15 changed files with 377 additions and 342 deletions

View File

@@ -171,7 +171,6 @@ pub struct RPCProcessorUnlockedInner {
queue_size: u32,
concurrency: u32,
max_route_hop_count: usize,
default_route_hop_count: usize,
validate_dial_info_receipt_time_ms: u32,
update_callback: UpdateCallback,
waiting_rpc_table: OperationWaiter<RPCMessage>,
@@ -208,7 +207,6 @@ impl RPCProcessor {
let queue_size = c.network.rpc.queue_size;
let timeout = ms_to_us(c.network.rpc.timeout_ms);
let max_route_hop_count = c.network.rpc.max_route_hop_count as usize;
let default_route_hop_count = c.network.rpc.default_route_hop_count as usize;
if concurrency == 0 {
concurrency = intf::get_concurrency() / 2;
if concurrency == 0 {
@@ -222,7 +220,6 @@ impl RPCProcessor {
queue_size,
concurrency,
max_route_hop_count,
default_route_hop_count,
validate_dial_info_receipt_time_ms,
update_callback,
waiting_rpc_table: OperationWaiter::new(),
@@ -418,7 +415,7 @@ impl RPCProcessor {
}
// Wrap an operation with a private route inside a safety route
pub(super) fn wrap_with_route(
fn wrap_with_route(
&self,
safety_selection: SafetySelection,
private_route: PrivateRoute,
@@ -540,6 +537,7 @@ impl RPCProcessor {
match safety_selection {
SafetySelection::Unsafe(sequencing) => {
// Apply safety selection sequencing requirement if it is more strict than the node_ref's sequencing requirement
let mut node_ref = node_ref.clone();
if sequencing > node_ref.sequencing() {
node_ref.set_sequencing(sequencing)
}

View File

@@ -62,22 +62,26 @@ impl RPCProcessor {
// add node information for the requesting node to our routing table
let routing_table = self.routing_table();
let network_manager = self.network_manager();
let has_valid_own_node_info =
routing_table.has_valid_own_node_info(RoutingDomain::PublicInternet);
let own_peer_info = routing_table.get_own_peer_info(RoutingDomain::PublicInternet);
// find N nodes closest to the target node in our routing table
let closest_nodes = routing_table.find_closest_nodes(
find_node_q.node_id,
// filter
|rti, _k, v| {
let filter = Box::new(
move |rti: &RoutingTableInner, _k: DHTKey, v: Option<Arc<BucketEntry>>| {
rti.filter_has_valid_signed_node_info(
RoutingDomain::PublicInternet,
has_valid_own_node_info,
v,
)
},
) as RoutingTableEntryFilter;
let filters = VecDeque::from([filter]);
let closest_nodes = routing_table.find_closest_nodes(
find_node_q.node_id,
filters,
// transform
|rti, k, v| {
rti.transform_to_peer_info(

View File

@@ -45,7 +45,7 @@ impl RPCProcessor {
RPCMessageHeaderDetail::PrivateRoute(detail) => {
network_result_value_or_log!(debug
network_manager
.handle_private_receipt(receipt)
.handle_private_receipt(receipt, detail.private_route)
.await => {}
);
}

View File

@@ -121,7 +121,8 @@ impl RPCProcessor {
if next_private_route.hop_count != 0 {
let node_id = self.routing_table.node_id();
let node_id_secret = self.routing_table.node_id_secret();
let sig = sign(&node_id, &node_id_secret, &route.operation.data).map_err(RPCError::internal)?;
let sig = sign(&node_id, &node_id_secret, &route.operation.data)
.map_err(RPCError::internal)?;
route.operation.signatures.push(sig);
}
@@ -169,14 +170,16 @@ impl RPCProcessor {
// If the private route public key is our node id, then this was sent via safety route to our node directly
// so there will be no signatures to validate
let opt_pr_info = if private_route.public_key == self.routing_table.node_id() {
let (secret_key, safety_selection) = if private_route.public_key
== self.routing_table.node_id()
{
// The private route was a stub
// Return our secret key and an appropriate safety selection
//
// Note: it is important that we never respond with a safety route to questions that come
// in without a private route. Giving away a safety route when the node id is known is
// in without a private route. Giving away a safety route when the node id is known is
// a privacy violation!
// Get sequencing preference
let sequencing = if detail
.connection_descriptor
@@ -187,65 +190,25 @@ impl RPCProcessor {
} else {
Sequencing::NoPreference
};
Some((
self.routing_table.node_id_secret(),
(
self.routing_table.node_id_secret(),
SafetySelection::Unsafe(sequencing),
))
)
} else {
// Get sender id
let sender_id = detail.envelope.get_sender_id();
// Look up the private route and ensure it's one in our spec store
let rss= self.routing_table.route_spec_store();
let opt_signatures_valid = rss.with_route_spec_detail(&private_route.public_key, |rsd| {
// Ensure we have the right number of signatures
if routed_operation.signatures.len() != rsd.hops.len() - 1 {
// Wrong number of signatures
log_rpc!(debug "wrong number of signatures ({} should be {}) for routed operation on private route {}", routed_operation.signatures.len(), rsd.hops.len() - 1, private_route.public_key);
return None;
}
// Validate signatures to ensure the route was handled by the nodes and not messed with
for (hop_n, hop_public_key) in rsd.hops.iter().enumerate() {
// The last hop is not signed, as the whole packet is signed
if hop_n == routed_operation.signatures.len() {
// Verify the node we received the routed operation from is the last hop in our route
if *hop_public_key != sender_id {
log_rpc!(debug "received routed operation from the wrong hop ({} should be {}) on private route {}", hop_public_key.encode(), sender_id.encode(), private_route.public_key);
return None;
}
} else {
// Verify a signature for a hop node along the route
if let Err(e) = verify(
hop_public_key,
&routed_operation.data,
&routed_operation.signatures[hop_n],
) {
log_rpc!(debug "failed to verify signature for hop {} at {} on private route {}", hop_n, hop_public_key, private_route.public_key);
return None;
}
}
}
// We got the correct signatures, return a key ans
Some((
rsd.secret_key,
SafetySelection::Safe(SafetySpec {
preferred_route: Some(private_route.public_key),
hop_count: rsd.hops.len(),
stability: rsd.stability,
sequencing: rsd.sequencing,
})
))
});
opt_signatures_valid.ok_or_else(|| {
RPCError::protocol("routed operation received on unallocated private route")
})?
let rss = self.routing_table.route_spec_store();
rss.validate_signatures(
&private_route.public_key,
&routed_operation.signatures,
&routed_operation.data,
sender_id,
)
.map_err(RPCError::protocol)?
.ok_or_else(|| RPCError::protocol("signatures did not validate for private route"))?
};
if opt_pr_info.is_none() {
return Err(RPCError::protocol(
"signatures did not validate for private route",
));
}
let (secret_key, safety_selection) = opt_pr_info.unwrap();
// 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
@@ -344,8 +307,13 @@ impl RPCProcessor {
.await?;
} else {
// Private route is empty, process routed operation
self.process_routed_operation(detail, route.operation, &route.safety_route, &private_route)
.await?;
self.process_routed_operation(
detail,
route.operation,
&route.safety_route,
&private_route,
)
.await?;
}
} else if blob_tag == 0 {
// RouteHop
@@ -410,8 +378,13 @@ impl RPCProcessor {
.await?;
} else {
// No hops left, time to process the routed operation
self.process_routed_operation(detail, route.operation, &route.safety_route, private_route)
.await?;
self.process_routed_operation(
detail,
route.operation,
&route.safety_route,
private_route,
)
.await?;
}
}
}

View File

@@ -34,7 +34,8 @@ impl RPCProcessor {
// Wait for receipt
match eventual_value.await.take_value().unwrap() {
ReceiptEvent::ReturnedPrivate | ReceiptEvent::ReturnedInBand { inbound_noderef: _ } => {
ReceiptEvent::ReturnedPrivate { private_route: _ }
| ReceiptEvent::ReturnedInBand { inbound_noderef: _ } => {
log_net!(debug "validate_dial_info receipt should be returned out-of-band".green());
Ok(false)
}
@@ -94,20 +95,26 @@ impl RPCProcessor {
routing_domain,
dial_info.clone(),
);
let will_validate_dial_info_filter = Box::new(move |_rti, e: &BucketEntryInner| {
if let Some(status) = &e.node_status(routing_domain) {
status.will_validate_dial_info()
} else {
true
}
});
let filter = RoutingTable::combine_entry_filters(
let will_validate_dial_info_filter = Box::new(
move |rti: &RoutingTableInner, _k: DHTKey, v: Option<Arc<BucketEntry>>| {
let entry = v.unwrap();
entry.with(rti, move |_rti, e| {
if let Some(status) = &e.node_status(routing_domain) {
status.will_validate_dial_info()
} else {
true
}
})
},
) as RoutingTableEntryFilter;
let filters = VecDeque::from([
outbound_dial_info_entry_filter,
will_validate_dial_info_filter, fuck this shit. do it tomorrow.
);
will_validate_dial_info_filter,
]);
// Find nodes matching filter to redirect this to
let peers = routing_table.find_fast_public_nodes_filtered(node_count, filter);
let peers = routing_table.find_fast_public_nodes_filtered(node_count, filters);
if peers.is_empty() {
return Err(RPCError::internal(format!(
"no peers able to reach dialinfo '{:?}'",