refactor rpc validation
This commit is contained in:
@@ -73,7 +73,9 @@ pub struct BucketEntryLocalNetwork {
|
||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||
pub struct BucketEntryInner {
|
||||
/// The node ids matching this bucket entry, with the cryptography versions supported by this node as the 'kind' field
|
||||
node_ids: TypedKeySet,
|
||||
validated_node_ids: TypedKeySet,
|
||||
/// The node ids claimed by the remote node that use cryptography versions we do not support
|
||||
unsupported_node_ids: TypedKeySet,
|
||||
/// The set of envelope versions supported by the node inclusive of the requirements of any relay the node may be using
|
||||
envelope_support: Vec<u8>,
|
||||
/// If this node has updated it's SignedNodeInfo since our network
|
||||
@@ -122,9 +124,11 @@ impl BucketEntryInner {
|
||||
self.node_ref_tracks.remove(&track_id);
|
||||
}
|
||||
|
||||
/// Get node ids
|
||||
/// Get all node ids
|
||||
pub fn node_ids(&self) -> TypedKeySet {
|
||||
self.node_ids.clone()
|
||||
let mut node_ids = self.validated_node_ids.clone();
|
||||
node_ids.add_all(&self.unsupported_node_ids);
|
||||
node_ids
|
||||
}
|
||||
|
||||
/// Add a node id for a particular crypto kind.
|
||||
@@ -132,33 +136,39 @@ impl BucketEntryInner {
|
||||
/// Returns Ok(None) if no previous existing node id was associated with that crypto kind
|
||||
/// Results Err() if this operation would add more crypto kinds than we support
|
||||
pub fn add_node_id(&mut self, node_id: TypedKey) -> EyreResult<Option<TypedKey>> {
|
||||
if let Some(old_node_id) = self.node_ids.get(node_id.kind) {
|
||||
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 this was already there we do nothing
|
||||
if old_node_id == node_id {
|
||||
return Ok(None);
|
||||
}
|
||||
// Won't change number of crypto kinds
|
||||
self.node_ids.add(node_id);
|
||||
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 self.node_ids.len() == MAX_CRYPTO_KINDS {
|
||||
if self.validated_node_ids.len() + self.unsupported_node_ids.len() == MAX_CRYPTO_KINDS {
|
||||
bail!("too many crypto kinds for this node");
|
||||
}
|
||||
self.node_ids.add(node_id);
|
||||
node_ids.add(node_id);
|
||||
Ok(None)
|
||||
}
|
||||
pub fn best_node_id(&self) -> TypedKey {
|
||||
self.node_ids.best().unwrap()
|
||||
self.validated_node_ids.best().unwrap()
|
||||
}
|
||||
|
||||
/// Get crypto kinds
|
||||
pub fn crypto_kinds(&self) -> Vec<CryptoKind> {
|
||||
self.node_ids.kinds()
|
||||
self.validated_node_ids.kinds()
|
||||
}
|
||||
/// Compare sets of crypto kinds
|
||||
pub fn common_crypto_kinds(&self, other: &[CryptoKind]) -> Vec<CryptoKind> {
|
||||
common_crypto_kinds(&self.node_ids.kinds(), other)
|
||||
common_crypto_kinds(&self.validated_node_ids.kinds(), other)
|
||||
}
|
||||
|
||||
|
||||
@@ -270,7 +280,7 @@ impl BucketEntryInner {
|
||||
}
|
||||
|
||||
// Update the envelope version support we have to use
|
||||
let envelope_support = signed_node_info.node_info().envelope_support.clone();
|
||||
let envelope_support = signed_node_info.node_info().envelope_support().to_vec();
|
||||
|
||||
// Update the signed node info
|
||||
*opt_current_sni = Some(Box::new(signed_node_info));
|
||||
@@ -333,8 +343,10 @@ impl BucketEntryInner {
|
||||
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
|
||||
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
|
||||
};
|
||||
// Peer info includes all node ids, even unvalidated ones
|
||||
let node_ids = self.node_ids();
|
||||
opt_current_sni.as_ref().map(|s| PeerInfo {
|
||||
node_ids: self.node_ids.clone(),
|
||||
node_ids,
|
||||
signed_node_info: *s.clone(),
|
||||
})
|
||||
}
|
||||
@@ -781,11 +793,13 @@ pub struct BucketEntry {
|
||||
impl BucketEntry {
|
||||
pub(super) fn new(first_node_id: TypedKey) -> Self {
|
||||
let now = get_aligned_timestamp();
|
||||
let mut node_ids = TypedKeySet::new();
|
||||
node_ids.add(first_node_id);
|
||||
let mut validated_node_ids = TypedKeySet::new();
|
||||
let mut unsupported_node_ids = TypedKeySet::new();
|
||||
validated_node_ids.add(first_node_id);
|
||||
|
||||
let inner = BucketEntryInner {
|
||||
node_ids,
|
||||
validated_node_ids,
|
||||
unsupported_node_ids,
|
||||
envelope_support: Vec::new(),
|
||||
updated_since_last_network_change: false,
|
||||
last_connections: BTreeMap::new(),
|
||||
|
||||
@@ -792,8 +792,8 @@ impl RoutingTable {
|
||||
e.with(rti, |_rti, e| {
|
||||
if let Some(ni) = e.node_info(routing_domain) {
|
||||
let dif = DialInfoFilter::all()
|
||||
.with_protocol_type_set(ni.outbound_protocols)
|
||||
.with_address_type_set(ni.address_types);
|
||||
.with_protocol_type_set(ni.outbound_protocols())
|
||||
.with_address_type_set(ni.address_types());
|
||||
if dial_info.matches_filter(&dif) {
|
||||
return true;
|
||||
}
|
||||
@@ -851,7 +851,7 @@ impl RoutingTable {
|
||||
// does it have some dial info we need?
|
||||
let filter = |n: &NodeInfo| {
|
||||
let mut keep = false;
|
||||
for did in &n.dial_info_detail_list {
|
||||
for did in n.dial_info_detail_list() {
|
||||
if matches!(did.dial_info.address_type(), AddressType::IPV4) {
|
||||
for (n, protocol_type) in protocol_types.iter().enumerate() {
|
||||
if nodes_proto_v4[n] < max_per_type
|
||||
@@ -974,12 +974,12 @@ impl RoutingTable {
|
||||
let mut out = Vec::<NodeRef>::with_capacity(peers.len());
|
||||
for p in peers {
|
||||
// Ensure we're getting back nodes we asked for
|
||||
if !p.node_ids.kinds().contains(&crypto_kind) {
|
||||
if !p.node_ids().kinds().contains(&crypto_kind) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't register our own node
|
||||
if self.matches_own_node_id(&p.node_ids) {
|
||||
if self.matches_own_node_id(p.node_ids()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -174,13 +174,13 @@ pub trait NodeRefBase: Sized {
|
||||
self.operate_mut(|_rti, e| e.set_our_node_info_ts(routing_domain, seen_ts));
|
||||
}
|
||||
fn network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
|
||||
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.network_class))
|
||||
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.network_class()))
|
||||
}
|
||||
fn outbound_protocols(&self, routing_domain: RoutingDomain) -> Option<ProtocolTypeSet> {
|
||||
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.outbound_protocols))
|
||||
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.outbound_protocols()))
|
||||
}
|
||||
fn address_types(&self, routing_domain: RoutingDomain) -> Option<AddressTypeSet> {
|
||||
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.address_types))
|
||||
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.address_types()))
|
||||
}
|
||||
fn node_info_outbound_filter(&self, routing_domain: RoutingDomain) -> DialInfoFilter {
|
||||
let mut dif = DialInfoFilter::all();
|
||||
@@ -199,7 +199,7 @@ pub trait NodeRefBase: Sized {
|
||||
.and_then(|rpi| {
|
||||
// If relay is ourselves, then return None, because we can't relay through ourselves
|
||||
// and to contact this node we should have had an existing inbound connection
|
||||
if rti.unlocked_inner.matches_own_node_id(&rpi.node_ids) {
|
||||
if rti.unlocked_inner.matches_own_node_id(rpi.node_ids()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,19 @@ pub enum RouteNode {
|
||||
}
|
||||
|
||||
impl RouteNode {
|
||||
pub fn validate(&self, crypto: Crypto) -> Result<(), VeilidAPIError> {
|
||||
match self {
|
||||
RouteNode::NodeId(_) => Ok(()),
|
||||
RouteNode::PeerInfo(pi) => {
|
||||
let validated_node_ids = pi.validate(crypto)?;
|
||||
if validated_node_ids.is_empty() {
|
||||
apibail_generic!("no validated node ids for route node");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_ref(
|
||||
&self,
|
||||
routing_table: RoutingTable,
|
||||
@@ -48,10 +61,10 @@ impl RouteNode {
|
||||
RouteNode::NodeId(id) => {
|
||||
format!("{}", TypedKey::new(crypto_kind, *id))
|
||||
}
|
||||
RouteNode::PeerInfo(pi) => match pi.node_ids.get(crypto_kind) {
|
||||
RouteNode::PeerInfo(pi) => match pi.node_ids().get(crypto_kind) {
|
||||
Some(id) => format!("{}", id),
|
||||
None => {
|
||||
format!("({})?{}", crypto_kind, pi.node_ids)
|
||||
format!("({})?{}", crypto_kind, pi.node_ids())
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -66,6 +79,11 @@ pub struct RouteHop {
|
||||
/// The encrypted blob to pass to the next hop as its data (None for stubs)
|
||||
pub next_hop: Option<RouteHopData>,
|
||||
}
|
||||
impl RouteHop {
|
||||
pub fn validate(&self, crypto: Crypto) -> Result<(), VeilidAPIError> {
|
||||
self.node.validate(crypto)
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind of hops a private route can have
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -78,6 +96,15 @@ pub enum PrivateRouteHops {
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl PrivateRouteHops {
|
||||
pub fn validate(&self, crypto: Crypto) -> Result<(), VeilidAPIError> {
|
||||
match self {
|
||||
PrivateRouteHops::FirstHop(rh) => rh.validate(crypto),
|
||||
PrivateRouteHops::Data(_) => Ok(()),
|
||||
PrivateRouteHops::Empty => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// A private route for receiver privacy
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PrivateRoute {
|
||||
@@ -108,6 +135,10 @@ impl PrivateRoute {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate(&self, crypto: Crypto) -> Result<(), VeilidAPIError> {
|
||||
self.hops.validate(crypto)
|
||||
}
|
||||
|
||||
/// Check if this is a stub route
|
||||
pub fn is_stub(&self) -> bool {
|
||||
if let PrivateRouteHops::FirstHop(first_hop) = &self.hops {
|
||||
@@ -155,7 +186,7 @@ impl PrivateRoute {
|
||||
// Get the safety route to use from the spec
|
||||
Some(match &pr_first_hop.node {
|
||||
RouteNode::NodeId(n) => TypedKey::new(self.public_key.kind, *n),
|
||||
RouteNode::PeerInfo(p) => p.node_ids.get(self.public_key.kind).unwrap(),
|
||||
RouteNode::PeerInfo(p) => p.node_ids().get(self.public_key.kind).unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1550,7 +1550,9 @@ impl RouteSpecStore {
|
||||
.get_root::<veilid_capnp::private_route::Reader>()
|
||||
.map_err(RPCError::internal)
|
||||
.wrap_err("failed to make reader for private_route")?;
|
||||
let private_route = decode_private_route(&pr_reader, crypto.clone()).wrap_err("failed to decode private route")?;
|
||||
let private_route = decode_private_route(&pr_reader).wrap_err("failed to decode private route")?;
|
||||
private_route.validate(crypto.clone()).wrap_err("failed to validate private route")?;
|
||||
|
||||
out.push(private_route);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,14 +102,14 @@ impl RoutingDomainDetailCommon {
|
||||
}
|
||||
|
||||
fn make_peer_info(&self, rti: &RoutingTableInner) -> PeerInfo {
|
||||
let node_info = NodeInfo {
|
||||
network_class: self.network_class.unwrap_or(NetworkClass::Invalid),
|
||||
outbound_protocols: self.outbound_protocols,
|
||||
address_types: self.address_types,
|
||||
envelope_support: VALID_ENVELOPE_VERSIONS.to_vec(),
|
||||
crypto_support: VALID_CRYPTO_KINDS.to_vec(),
|
||||
dial_info_detail_list: self.dial_info_details.clone(),
|
||||
};
|
||||
let node_info = NodeInfo::new(
|
||||
self.network_class.unwrap_or(NetworkClass::Invalid),
|
||||
self.outbound_protocols,
|
||||
self.address_types,
|
||||
VALID_ENVELOPE_VERSIONS.to_vec(),
|
||||
VALID_CRYPTO_KINDS.to_vec(),
|
||||
self.dial_info_details.clone()
|
||||
);
|
||||
|
||||
let relay_info = self
|
||||
.relay_node
|
||||
@@ -117,8 +117,9 @@ impl RoutingDomainDetailCommon {
|
||||
.and_then(|rn| {
|
||||
let opt_relay_pi = rn.locked(rti).make_peer_info(self.routing_domain);
|
||||
if let Some(relay_pi) = opt_relay_pi {
|
||||
match relay_pi.signed_node_info {
|
||||
SignedNodeInfo::Direct(d) => Some((relay_pi.node_ids, d)),
|
||||
let (relay_ids, relay_sni) = relay_pi.into_fields();
|
||||
match relay_sni {
|
||||
SignedNodeInfo::Direct(d) => Some((relay_ids, d)),
|
||||
SignedNodeInfo::Relayed(_) => {
|
||||
warn!("relay node should not have a relay itself! if this happens, a relay updated its signed node info and became a relay, which should cause the relay to be dropped");
|
||||
None
|
||||
@@ -230,8 +231,8 @@ fn first_filtered_dial_info_detail(
|
||||
) -> Option<DialInfoDetail> {
|
||||
let dial_info_filter = dial_info_filter.clone().filtered(
|
||||
&DialInfoFilter::all()
|
||||
.with_address_type_set(from_node.address_types)
|
||||
.with_protocol_type_set(from_node.outbound_protocols),
|
||||
.with_address_type_set(from_node.address_types())
|
||||
.with_protocol_type_set(from_node.outbound_protocols()),
|
||||
);
|
||||
|
||||
// Get first filtered dialinfo
|
||||
@@ -278,18 +279,18 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
|
||||
sequencing: Sequencing,
|
||||
) -> ContactMethod {
|
||||
// Get the nodeinfos for convenience
|
||||
let node_a = peer_a.signed_node_info.node_info();
|
||||
let node_b = peer_b.signed_node_info.node_info();
|
||||
let node_a = peer_a.signed_node_info().node_info();
|
||||
let node_b = peer_b.signed_node_info().node_info();
|
||||
|
||||
// Get the node ids that would be used between these peers
|
||||
let cck = common_crypto_kinds(&peer_a.node_ids.kinds(), &peer_b.node_ids.kinds());
|
||||
let cck = common_crypto_kinds(&peer_a.node_ids().kinds(), &peer_b.node_ids().kinds());
|
||||
let Some(best_ck) = cck.first().copied() else {
|
||||
// No common crypto kinds between these nodes, can't contact
|
||||
return ContactMethod::Unreachable;
|
||||
};
|
||||
|
||||
//let node_a_id = peer_a.node_ids.get(best_ck).unwrap();
|
||||
let node_b_id = peer_b.node_ids.get(best_ck).unwrap();
|
||||
//let node_a_id = peer_a.node_ids().get(best_ck).unwrap();
|
||||
let node_b_id = peer_b.node_ids().get(best_ck).unwrap();
|
||||
|
||||
// Get the best match dial info for node B if we have it
|
||||
if let Some(target_did) =
|
||||
@@ -302,17 +303,17 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
|
||||
}
|
||||
|
||||
// Get the target's inbound relay, it must have one or it is not reachable
|
||||
if let Some(node_b_relay) = peer_b.signed_node_info.relay_info() {
|
||||
if let Some(node_b_relay) = peer_b.signed_node_info().relay_info() {
|
||||
|
||||
// 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 peer_b.signed_node_info.relay_ids().contains_any(&peer_a.node_ids) {
|
||||
if peer_b.signed_node_info().relay_ids().contains_any(peer_a.node_ids()) {
|
||||
return ContactMethod::Existing;
|
||||
}
|
||||
|
||||
// Get best node id to contact relay with
|
||||
let Some(node_b_relay_id) = peer_b.signed_node_info.relay_ids().get(best_ck) else {
|
||||
let Some(node_b_relay_id) = peer_b.signed_node_info().relay_ids().get(best_ck) else {
|
||||
// No best relay id
|
||||
return ContactMethod::Unreachable;
|
||||
};
|
||||
@@ -327,7 +328,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
|
||||
.is_some()
|
||||
{
|
||||
// Can node A receive anything inbound ever?
|
||||
if matches!(node_a.network_class, NetworkClass::InboundCapable) {
|
||||
if matches!(node_a.network_class(), NetworkClass::InboundCapable) {
|
||||
///////// Reverse connection
|
||||
|
||||
// Get the best match dial info for an reverse inbound connection from node B to node A
|
||||
@@ -390,17 +391,17 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
|
||||
}
|
||||
}
|
||||
// If the node B has no direct dial info, it needs to have an inbound relay
|
||||
else if let Some(node_b_relay) = peer_b.signed_node_info.relay_info() {
|
||||
else if let Some(node_b_relay) = peer_b.signed_node_info().relay_info() {
|
||||
|
||||
// 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 peer_b.signed_node_info.relay_ids().contains_any(&peer_a.node_ids) {
|
||||
if peer_b.signed_node_info().relay_ids().contains_any(peer_a.node_ids()) {
|
||||
return ContactMethod::Existing;
|
||||
}
|
||||
|
||||
// Get best node id to contact relay with
|
||||
let Some(node_b_relay_id) = peer_b.signed_node_info.relay_ids().get(best_ck) else {
|
||||
let Some(node_b_relay_id) = peer_b.signed_node_info().relay_ids().get(best_ck) else {
|
||||
// No best relay id
|
||||
return ContactMethod::Unreachable;
|
||||
};
|
||||
@@ -419,7 +420,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
|
||||
}
|
||||
|
||||
// If node A can't reach the node by other means, it may need to use its own relay
|
||||
if let Some(node_a_relay_id) = peer_a.signed_node_info.relay_ids().get(best_ck) {
|
||||
if let Some(node_a_relay_id) = peer_a.signed_node_info().relay_ids().get(best_ck) {
|
||||
return ContactMethod::OutboundRelay(node_a_relay_id);
|
||||
}
|
||||
|
||||
@@ -484,8 +485,8 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail {
|
||||
// Scope the filter down to protocols node A can do outbound
|
||||
let dial_info_filter = dial_info_filter.filtered(
|
||||
&DialInfoFilter::all()
|
||||
.with_address_type_set(peer_a.signed_node_info.node_info().address_types)
|
||||
.with_protocol_type_set(peer_a.signed_node_info.node_info().outbound_protocols),
|
||||
.with_address_type_set(peer_a.signed_node_info().node_info().address_types())
|
||||
.with_protocol_type_set(peer_a.signed_node_info().node_info().outbound_protocols()),
|
||||
);
|
||||
|
||||
// Get first filtered dialinfo
|
||||
@@ -509,7 +510,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail {
|
||||
|
||||
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
|
||||
|
||||
let opt_target_did = peer_b.signed_node_info.node_info().first_filtered_dial_info_detail(sort, filter);
|
||||
let opt_target_did = peer_b.signed_node_info().node_info().first_filtered_dial_info_detail(sort, filter);
|
||||
if let Some(target_did) = opt_target_did {
|
||||
return ContactMethod::Direct(target_did.dial_info);
|
||||
}
|
||||
|
||||
@@ -171,11 +171,11 @@ impl RoutingTableInner {
|
||||
node_info: &NodeInfo,
|
||||
) -> bool {
|
||||
// Should not be passing around nodeinfo with an invalid network class
|
||||
if matches!(node_info.network_class, NetworkClass::Invalid) {
|
||||
if matches!(node_info.network_class(), NetworkClass::Invalid) {
|
||||
return false;
|
||||
}
|
||||
// Ensure all of the dial info works in this routing domain
|
||||
for did in &node_info.dial_info_detail_list {
|
||||
for did in node_info.dial_info_detail_list() {
|
||||
if !self.ensure_dial_info_is_valid(routing_domain, &did.dial_info) {
|
||||
return false;
|
||||
}
|
||||
@@ -258,7 +258,7 @@ impl RoutingTableInner {
|
||||
} else {
|
||||
Some(
|
||||
rdd.common()
|
||||
.with_peer_info(self, |pi| pi.signed_node_info.timestamp()),
|
||||
.with_peer_info(self, |pi| pi.signed_node_info().timestamp()),
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -804,13 +804,16 @@ impl RoutingTableInner {
|
||||
allow_invalid: bool,
|
||||
) -> Option<NodeRef> {
|
||||
// if our own node if is in the list then ignore it, as we don't add ourselves to our own routing table
|
||||
if self.unlocked_inner.matches_own_node_id(&peer_info.node_ids) {
|
||||
if self
|
||||
.unlocked_inner
|
||||
.matches_own_node_id(peer_info.node_ids())
|
||||
{
|
||||
log_rtab!(debug "can't register own node id in routing table");
|
||||
return None;
|
||||
}
|
||||
|
||||
// node can not be its own relay
|
||||
let rids = peer_info.signed_node_info.relay_ids();
|
||||
let rids = peer_info.signed_node_info().relay_ids();
|
||||
if self.unlocked_inner.matches_own_node_id(&rids) {
|
||||
log_rtab!(debug "node can not be its own relay");
|
||||
return None;
|
||||
@@ -818,22 +821,22 @@ impl RoutingTableInner {
|
||||
|
||||
if !allow_invalid {
|
||||
// verify signature
|
||||
if !peer_info.signed_node_info.has_any_signature() {
|
||||
log_rtab!(debug "signed node info for {:?} has invalid signature", &peer_info.node_ids);
|
||||
if !peer_info.signed_node_info().has_any_signature() {
|
||||
log_rtab!(debug "signed node info for {:?} has no valid signature", peer_info.node_ids());
|
||||
return None;
|
||||
}
|
||||
// verify signed node info is valid in this routing domain
|
||||
if !self.signed_node_info_is_valid_in_routing_domain(
|
||||
routing_domain,
|
||||
&peer_info.signed_node_info,
|
||||
peer_info.signed_node_info(),
|
||||
) {
|
||||
log_rtab!(debug "signed node info for {:?} not valid in the {:?} routing domain", peer_info.node_ids, routing_domain);
|
||||
log_rtab!(debug "signed node info for {:?} not valid in the {:?} routing domain", peer_info.node_ids(), routing_domain);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
self.create_node_ref(outer_self, &peer_info.node_ids, |_rti, e| {
|
||||
e.update_signed_node_info(routing_domain, peer_info.signed_node_info);
|
||||
self.create_node_ref(outer_self, peer_info.node_ids(), |_rti, e| {
|
||||
e.update_signed_node_info(routing_domain, peer_info.into_signed_node_info());
|
||||
})
|
||||
.map(|mut nr| {
|
||||
nr.set_filter(Some(
|
||||
|
||||
@@ -13,8 +13,8 @@ impl RoutingTable {
|
||||
let Some(own_peer_info) = self.get_own_peer_info(RoutingDomain::PublicInternet) else {
|
||||
return Ok(());
|
||||
};
|
||||
let own_node_info = own_peer_info.signed_node_info.node_info();
|
||||
let network_class = own_node_info.network_class;
|
||||
let own_node_info = own_peer_info.signed_node_info().node_info();
|
||||
let network_class = own_node_info.network_class();
|
||||
|
||||
// Get routing domain editor
|
||||
let mut editor = self.edit_routing_domain(RoutingDomain::PublicInternet);
|
||||
|
||||
@@ -1,19 +1,58 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||
#[derive(
|
||||
Clone, Default, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
|
||||
)]
|
||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||
pub struct NodeInfo {
|
||||
pub network_class: NetworkClass,
|
||||
network_class: NetworkClass,
|
||||
#[with(RkyvEnumSet)]
|
||||
pub outbound_protocols: ProtocolTypeSet,
|
||||
outbound_protocols: ProtocolTypeSet,
|
||||
#[with(RkyvEnumSet)]
|
||||
pub address_types: AddressTypeSet,
|
||||
pub envelope_support: Vec<u8>,
|
||||
pub crypto_support: Vec<CryptoKind>,
|
||||
pub dial_info_detail_list: Vec<DialInfoDetail>,
|
||||
address_types: AddressTypeSet,
|
||||
envelope_support: Vec<u8>,
|
||||
crypto_support: Vec<CryptoKind>,
|
||||
dial_info_detail_list: Vec<DialInfoDetail>,
|
||||
}
|
||||
|
||||
impl NodeInfo {
|
||||
pub fn new(
|
||||
network_class: NetworkClass,
|
||||
outbound_protocols: ProtocolTypeSet,
|
||||
address_types: AddressTypeSet,
|
||||
envelope_support: Vec<u8>,
|
||||
crypto_support: Vec<CryptoKind>,
|
||||
dial_info_detail_list: Vec<DialInfoDetail>,
|
||||
) -> Self {
|
||||
Self {
|
||||
network_class,
|
||||
outbound_protocols,
|
||||
address_types,
|
||||
envelope_support,
|
||||
crypto_support,
|
||||
dial_info_detail_list,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn network_class(&self) -> NetworkClass {
|
||||
self.network_class
|
||||
}
|
||||
pub fn outbound_protocols(&self) -> ProtocolTypeSet {
|
||||
self.outbound_protocols
|
||||
}
|
||||
pub fn address_types(&self) -> AddressTypeSet {
|
||||
self.address_types
|
||||
}
|
||||
pub fn envelope_support(&self) -> &[u8] {
|
||||
&self.envelope_support
|
||||
}
|
||||
pub fn crypto_support(&self) -> &[CryptoKind] {
|
||||
&self.crypto_support
|
||||
}
|
||||
pub fn dial_info_detail_list(&self) -> &[DialInfoDetail] {
|
||||
&self.dial_info_detail_list
|
||||
}
|
||||
|
||||
pub fn first_filtered_dial_info_detail<S, F>(
|
||||
&self,
|
||||
sort: Option<S>,
|
||||
|
||||
@@ -3,8 +3,8 @@ use super::*;
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||
pub struct PeerInfo {
|
||||
pub node_ids: TypedKeySet,
|
||||
pub signed_node_info: SignedNodeInfo,
|
||||
node_ids: TypedKeySet,
|
||||
signed_node_info: SignedNodeInfo,
|
||||
}
|
||||
|
||||
impl PeerInfo {
|
||||
@@ -15,4 +15,21 @@ impl PeerInfo {
|
||||
signed_node_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate(&self, crypto: Crypto) -> Result<TypedKeySet, VeilidAPIError> {
|
||||
self.signed_node_info.validate(&self.node_ids, crypto)
|
||||
}
|
||||
|
||||
pub fn node_ids(&self) -> &TypedKeySet {
|
||||
&self.node_ids
|
||||
}
|
||||
pub fn signed_node_info(&self) -> &SignedNodeInfo {
|
||||
&self.signed_node_info
|
||||
}
|
||||
pub fn into_signed_node_info(self) -> SignedNodeInfo {
|
||||
self.signed_node_info
|
||||
}
|
||||
pub fn into_fields(self) -> (TypedKeySet, SignedNodeInfo) {
|
||||
(self.node_ids, self.signed_node_info)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,36 +4,37 @@ use super::*;
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||
pub struct SignedDirectNodeInfo {
|
||||
pub node_info: NodeInfo,
|
||||
pub timestamp: Timestamp,
|
||||
pub signatures: Vec<TypedSignature>,
|
||||
node_info: NodeInfo,
|
||||
timestamp: Timestamp,
|
||||
signatures: Vec<TypedSignature>,
|
||||
}
|
||||
impl SignedDirectNodeInfo {
|
||||
/// 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(
|
||||
pub fn new(node_info: NodeInfo, timestamp: Timestamp, signatures: Vec<TypedSignature>) -> Self {
|
||||
Self {
|
||||
node_info,
|
||||
timestamp,
|
||||
signatures,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate(
|
||||
&self,
|
||||
node_ids: &TypedKeySet,
|
||||
crypto: Crypto,
|
||||
node_ids: &mut TypedKeySet,
|
||||
node_info: NodeInfo,
|
||||
timestamp: Timestamp,
|
||||
typed_signatures: Vec<TypedSignature>,
|
||||
) -> Result<Self, VeilidAPIError> {
|
||||
let node_info_bytes = Self::make_signature_bytes(&node_info, timestamp)?;
|
||||
) -> Result<TypedKeySet, VeilidAPIError> {
|
||||
let node_info_bytes = Self::make_signature_bytes(&self.node_info, self.timestamp)?;
|
||||
|
||||
// Verify the signatures that we can
|
||||
let validated_node_ids =
|
||||
crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?;
|
||||
*node_ids = validated_node_ids;
|
||||
if node_ids.len() == 0 {
|
||||
crypto.verify_signatures(node_ids, &node_info_bytes, &self.signatures)?;
|
||||
if validated_node_ids.len() == 0 {
|
||||
apibail_generic!("no valid node ids in direct node info");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
node_info,
|
||||
timestamp,
|
||||
signatures: typed_signatures,
|
||||
})
|
||||
Ok(validated_node_ids)
|
||||
}
|
||||
|
||||
pub fn make_signatures(
|
||||
@@ -83,4 +84,14 @@ impl SignedDirectNodeInfo {
|
||||
pub fn has_any_signature(&self) -> bool {
|
||||
!self.signatures.is_empty()
|
||||
}
|
||||
|
||||
pub fn node_info(&self) -> &NodeInfo {
|
||||
&self.node_info
|
||||
}
|
||||
pub fn timestamp(&self) -> Timestamp {
|
||||
self.timestamp
|
||||
}
|
||||
pub fn signatures(&self) -> &[TypedSignature] {
|
||||
&self.signatures
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,17 @@ pub enum SignedNodeInfo {
|
||||
}
|
||||
|
||||
impl SignedNodeInfo {
|
||||
pub fn validate(
|
||||
&self,
|
||||
node_ids: &TypedKeySet,
|
||||
crypto: Crypto,
|
||||
) -> Result<TypedKeySet, VeilidAPIError> {
|
||||
match self {
|
||||
SignedNodeInfo::Direct(d) => d.validate(node_ids, crypto),
|
||||
SignedNodeInfo::Relayed(r) => r.validate(node_ids, crypto),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_any_signature(&self) -> bool {
|
||||
match self {
|
||||
SignedNodeInfo::Direct(d) => d.has_any_signature(),
|
||||
@@ -17,34 +28,34 @@ impl SignedNodeInfo {
|
||||
|
||||
pub fn timestamp(&self) -> Timestamp {
|
||||
match self {
|
||||
SignedNodeInfo::Direct(d) => d.timestamp,
|
||||
SignedNodeInfo::Relayed(r) => r.timestamp,
|
||||
SignedNodeInfo::Direct(d) => d.timestamp(),
|
||||
SignedNodeInfo::Relayed(r) => r.timestamp(),
|
||||
}
|
||||
}
|
||||
pub fn node_info(&self) -> &NodeInfo {
|
||||
match self {
|
||||
SignedNodeInfo::Direct(d) => &d.node_info,
|
||||
SignedNodeInfo::Relayed(r) => &r.node_info,
|
||||
SignedNodeInfo::Direct(d) => &d.node_info(),
|
||||
SignedNodeInfo::Relayed(r) => &r.node_info(),
|
||||
}
|
||||
}
|
||||
pub fn relay_ids(&self) -> TypedKeySet {
|
||||
match self {
|
||||
SignedNodeInfo::Direct(_) => TypedKeySet::new(),
|
||||
SignedNodeInfo::Relayed(r) => r.relay_ids.clone(),
|
||||
SignedNodeInfo::Relayed(r) => r.relay_ids().clone(),
|
||||
}
|
||||
}
|
||||
pub fn relay_info(&self) -> Option<&NodeInfo> {
|
||||
match self {
|
||||
SignedNodeInfo::Direct(_) => None,
|
||||
SignedNodeInfo::Relayed(r) => Some(&r.relay_info.node_info),
|
||||
SignedNodeInfo::Relayed(r) => Some(r.relay_info().node_info()),
|
||||
}
|
||||
}
|
||||
pub fn relay_peer_info(&self) -> Option<PeerInfo> {
|
||||
match self {
|
||||
SignedNodeInfo::Direct(_) => None,
|
||||
SignedNodeInfo::Relayed(r) => Some(PeerInfo::new(
|
||||
r.relay_ids.clone(),
|
||||
SignedNodeInfo::Direct(r.relay_info.clone()),
|
||||
r.relay_ids().clone(),
|
||||
SignedNodeInfo::Direct(r.relay_info().clone()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -58,7 +69,7 @@ impl SignedNodeInfo {
|
||||
|
||||
pub fn has_sequencing_matched_dial_info(&self, sequencing: Sequencing) -> bool {
|
||||
// Check our dial info
|
||||
for did in &self.node_info().dial_info_detail_list {
|
||||
for did in self.node_info().dial_info_detail_list() {
|
||||
match sequencing {
|
||||
Sequencing::NoPreference | Sequencing::PreferOrdered => return true,
|
||||
Sequencing::EnsureOrdered => {
|
||||
@@ -72,7 +83,7 @@ impl SignedNodeInfo {
|
||||
return self
|
||||
.relay_info()
|
||||
.map(|relay_ni| {
|
||||
for did in &relay_ni.dial_info_detail_list {
|
||||
for did in relay_ni.dial_info_detail_list() {
|
||||
match sequencing {
|
||||
Sequencing::NoPreference | Sequencing::PreferOrdered => return true,
|
||||
Sequencing::EnsureOrdered => {
|
||||
|
||||
@@ -4,11 +4,11 @@ use super::*;
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
|
||||
#[archive_attr(repr(C), derive(CheckBytes))]
|
||||
pub struct SignedRelayedNodeInfo {
|
||||
pub node_info: NodeInfo,
|
||||
pub relay_ids: TypedKeySet,
|
||||
pub relay_info: SignedDirectNodeInfo,
|
||||
pub timestamp: Timestamp,
|
||||
pub signatures: Vec<TypedSignature>,
|
||||
node_info: NodeInfo,
|
||||
relay_ids: TypedKeySet,
|
||||
relay_info: SignedDirectNodeInfo,
|
||||
timestamp: Timestamp,
|
||||
signatures: Vec<TypedSignature>,
|
||||
}
|
||||
|
||||
impl SignedRelayedNodeInfo {
|
||||
@@ -16,30 +16,50 @@ impl SignedRelayedNodeInfo {
|
||||
/// 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,
|
||||
node_ids: &mut TypedKeySet,
|
||||
node_info: NodeInfo,
|
||||
relay_ids: TypedKeySet,
|
||||
relay_info: SignedDirectNodeInfo,
|
||||
timestamp: Timestamp,
|
||||
typed_signatures: Vec<TypedSignature>,
|
||||
) -> Result<Self, VeilidAPIError> {
|
||||
let node_info_bytes =
|
||||
Self::make_signature_bytes(&node_info, &relay_ids, &relay_info, timestamp)?;
|
||||
let validated_node_ids =
|
||||
crypto.verify_signatures(node_ids, &node_info_bytes, &typed_signatures)?;
|
||||
*node_ids = validated_node_ids;
|
||||
if node_ids.len() == 0 {
|
||||
apibail_generic!("no valid node ids in relayed node info");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
signatures: Vec<TypedSignature>,
|
||||
) -> Self {
|
||||
Self {
|
||||
node_info,
|
||||
relay_ids,
|
||||
relay_info,
|
||||
timestamp,
|
||||
signatures: typed_signatures,
|
||||
})
|
||||
signatures,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate(
|
||||
&self,
|
||||
node_ids: &TypedKeySet,
|
||||
crypto: Crypto,
|
||||
) -> Result<TypedKeySet, VeilidAPIError> {
|
||||
// Ensure the relay info for the node has a superset of the crypto kinds of the node it is relaying
|
||||
if common_crypto_kinds(
|
||||
self.node_info.crypto_support(),
|
||||
self.relay_info.node_info().crypto_support(),
|
||||
)
|
||||
.len()
|
||||
!= self.node_info.crypto_support().len()
|
||||
{
|
||||
apibail_generic!("relay should have superset of node crypto kinds");
|
||||
}
|
||||
|
||||
// Verify signatures
|
||||
let node_info_bytes = Self::make_signature_bytes(
|
||||
&self.node_info,
|
||||
&self.relay_ids,
|
||||
&self.relay_info,
|
||||
self.timestamp,
|
||||
)?;
|
||||
let validated_node_ids =
|
||||
crypto.verify_signatures(node_ids, &node_info_bytes, &self.signatures)?;
|
||||
if validated_node_ids.len() == 0 {
|
||||
apibail_generic!("no valid node ids in relayed node info");
|
||||
}
|
||||
Ok(validated_node_ids)
|
||||
}
|
||||
|
||||
pub fn make_signatures(
|
||||
@@ -103,4 +123,20 @@ impl SignedRelayedNodeInfo {
|
||||
pub fn has_any_signature(&self) -> bool {
|
||||
!self.signatures.is_empty()
|
||||
}
|
||||
|
||||
pub fn node_info(&self) -> &NodeInfo {
|
||||
&self.node_info
|
||||
}
|
||||
pub fn timestamp(&self) -> Timestamp {
|
||||
self.timestamp
|
||||
}
|
||||
pub fn relay_ids(&self) -> &TypedKeySet {
|
||||
&self.relay_ids
|
||||
}
|
||||
pub fn relay_info(&self) -> &SignedDirectNodeInfo {
|
||||
&self.relay_info
|
||||
}
|
||||
pub fn signatures(&self) -> &[TypedSignature] {
|
||||
&self.signatures
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user