refactor rpc validation

This commit is contained in:
John Smith
2023-04-20 11:47:54 -04:00
parent b4a071170d
commit 7f909a06b9
52 changed files with 729 additions and 430 deletions
+29 -15
View File
@@ -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(),
+5 -5
View File
@@ -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;
}
+4 -4
View File
@@ -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;
}
+34 -3
View File
@@ -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
}
}