From e3fe44004a694154be3359676affcfa956193f22 Mon Sep 17 00:00:00 2001 From: qima Date: Tue, 2 Apr 2024 19:56:04 +0800 Subject: [PATCH] feat(node): check peer quotes are properly signed --- Cargo.lock | 1 + sn_networking/src/lib.rs | 5 ++++ sn_node/src/quote.rs | 10 ++++++- sn_transfers/Cargo.toml | 1 + sn_transfers/src/wallet/data_payments.rs | 37 ++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a7f37229bf..508d1e6add 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5453,6 +5453,7 @@ dependencies = [ "fs2", "hex", "lazy_static", + "libp2p", "pprof", "rand", "rayon", diff --git a/sn_networking/src/lib.rs b/sn_networking/src/lib.rs index e8afe65c9e..1ed38218ea 100644 --- a/sn_networking/src/lib.rs +++ b/sn_networking/src/lib.rs @@ -170,6 +170,11 @@ impl Network { self.keypair.public().verify(msg, sig) } + /// Returns the protobuf serialised PublicKey to allow messaging out for share. + pub fn get_pub_key(&self) -> Vec { + self.keypair.public().encode_protobuf() + } + /// Dial the given peer at the given address. /// This function will only be called for the bootstrap nodes. pub async fn dial(&self, addr: Multiaddr) -> Result<()> { diff --git a/sn_node/src/quote.rs b/sn_node/src/quote.rs index 3610d3ee87..4374be3182 100644 --- a/sn_node/src/quote.rs +++ b/sn_node/src/quote.rs @@ -33,6 +33,7 @@ impl Node { cost, timestamp, quoting_metrics: quoting_metrics.clone(), + pub_key: network.get_pub_key(), signature, }; @@ -104,7 +105,14 @@ pub(crate) async fn quotes_verification(network: &Network, quotes: Vec<(PeerId, quote.timestamp + time_gap > self_quote.timestamp }; - is_same_target && is_not_self && is_not_zero_quote && is_around_same_time + let is_signed_by_the_claimed_peer = + quote.check_is_signed_by_claimed_peer(*peer_id); + + is_same_target + && is_not_self + && is_not_zero_quote + && is_around_same_time + && is_signed_by_the_claimed_peer }) .cloned() .collect(); diff --git a/sn_transfers/Cargo.toml b/sn_transfers/Cargo.toml index 34b889205f..d021324796 100644 --- a/sn_transfers/Cargo.toml +++ b/sn_transfers/Cargo.toml @@ -16,6 +16,7 @@ custom_debug = "~0.5.0" dirs-next = "~2.0.0" hex = "~0.4.3" lazy_static = "~1.4.0" +libp2p = { version="0.53", features = ["identify", "kad"] } rand = { version = "~0.8.5", features = ["small_rng"] } rmp-serde = "1.1.1" serde = { version = "1.0.133", features = [ "derive", "rc" ]} diff --git a/sn_transfers/src/wallet/data_payments.rs b/sn_transfers/src/wallet/data_payments.rs index ec4847c600..08b20c084b 100644 --- a/sn_transfers/src/wallet/data_payments.rs +++ b/sn_transfers/src/wallet/data_payments.rs @@ -7,6 +7,7 @@ // permissions and limitations relating to use of the SAFE Network Software. use crate::{MainPubkey, NanoTokens, Transfer}; +use libp2p::{identity::PublicKey, PeerId}; use serde::{Deserialize, Serialize}; use std::time::SystemTime; use xor_name::XorName; @@ -102,6 +103,9 @@ pub struct PaymentQuote { pub timestamp: SystemTime, /// quoting metrics being used to generate this quote pub quoting_metrics: QuotingMetrics, + /// node's public key that can verify the signature + #[debug(skip)] + pub pub_key: Vec, #[debug(skip)] pub signature: QuoteSignature, } @@ -114,6 +118,7 @@ impl PaymentQuote { cost: NanoTokens::zero(), timestamp: SystemTime::now(), quoting_metrics: Default::default(), + pub_key: vec![], signature: vec![], } } @@ -142,6 +147,37 @@ impl PaymentQuote { bytes } + /// Check self is signed by the claimed peer + pub fn check_is_signed_by_claimed_peer(&self, claimed_peer: PeerId) -> bool { + let pub_key = if let Ok(pub_key) = PublicKey::try_decode_protobuf(&self.pub_key) { + pub_key + } else { + error!("Cann't parse PublicKey from protobuf"); + return false; + }; + + let self_peer_id = PeerId::from(pub_key.clone()); + + if self_peer_id != claimed_peer { + error!("This quote {self:?} of {self_peer_id:?} is not signed by {claimed_peer:?}"); + return false; + } + + let bytes = Self::bytes_for_signing( + self.content, + self.cost, + self.timestamp, + &self.quoting_metrics, + ); + + if !pub_key.verify(&bytes, &self.signature) { + error!("Signature is not signed by claimed pub_key"); + return false; + } + + true + } + /// Returns true) if the quote has not yet expired pub fn has_expired(&self) -> bool { let now = std::time::SystemTime::now(); @@ -160,6 +196,7 @@ impl PaymentQuote { cost, timestamp: SystemTime::now(), quoting_metrics: Default::default(), + pub_key: vec![], signature: vec![], } }