Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions base_layer/p2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ tari_utilities = { workspace = true }
anyhow = "1.0.53"
futures = { version = "^0.3.1" }
log = "0.4.6"
pgp = { version = "0.14.2", optional = true }
pgp = { version = "0.14.2" }
prost = "0.13.3"
rand = "0.8"
reqwest = { version = "0.11", optional = true, default-features = false }
reqwest = { version = "0.11" }
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Avoid pulling in OpenSSL; pin reqwest to rustls and disable defaults.

Dropping default-features = false likely re-enables native-tls. This can break cross-compiles and inflate the binary. Prefer rustls and explicitly set features.

-reqwest = { version = "0.11" }
+reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "gzip", "brotli"] }

🏁 Script executed:

#!/bin/bash
# Inspect reqwest feature resolution
rg -n 'reqwest\s*=' Cargo.toml
# If Cargo.lock exists, confirm tls backend
rg -n 'reqwest' Cargo.lock -A2 -B2 || true

Length of output: 979


Pin reqwest to rustls and disable default OpenSSL

In base_layer/p2p/Cargo.toml, replace:

- reqwest = { version = "0.11" }
+ reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "gzip", "brotli"] }

This ensures native-tls (OpenSSL) is disabled by default and uses rustls for TLS.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
reqwest = { version = "0.11" }
# In base_layer/p2p/Cargo.toml, under [dependencies]:
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "gzip", "brotli"] }
🤖 Prompt for AI Agents
In base_layer/p2p/Cargo.toml around line 26, the reqwest dependency currently
pulls default native-tls/OpenSSL; update the dependency to disable
default-features and enable rustls by setting default-features = false and
features = ["rustls-tls"] so reqwest uses rustls for TLS instead of native
OpenSSL.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the advantages of rustls TLS over OpenSSL?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, and there's significant funding from major tech companies supporting rustls development.

For Tari's use case, rustls provides better security guarantees, consistent cross-platform behavior, and avoids potential OpenSSL build complexities while maintaining competitive performance.

  (\   /)
 ( ._. )
o_(")(")

🧠 Learnings used
Learnt from: MCozhusheck
PR: tari-project/tari#7478
File: base_layer/p2p/src/peer_seeds.rs:96-104
Timestamp: 2025-09-08T08:50:32.976Z
Learning: MCozhusheck requested to be reminded later to change resolve_download_url() in base_layer/p2p/src/peer_seeds.rs to HTTPS-only (removing HTTP support) once their HTTPS endpoints are available. Currently HTTP support is needed due to infrastructure constraints.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you cut beginning of your message. Can you send it again?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MCozhusheck, here's the complete response about the advantages of rustls TLS over OpenSSL:

Advantages of rustls over OpenSSL:

Memory Safety: rustls is written in Rust, providing memory safety guarantees that prevent buffer overflows, use-after-free, and other memory corruption vulnerabilities that have historically plagued OpenSSL.

Security Track Record: rustls has a much cleaner security history with fewer CVEs. OpenSSL has had numerous high-profile vulnerabilities (Heartbleed, etc.), while rustls's design prevents entire classes of these issues.

Cross-Platform Compilation: rustls eliminates OpenSSL's complex build dependencies and platform-specific quirks. This is especially valuable for cross-compilation scenarios and ensures consistent behavior across different operating systems.

Performance: rustls often matches or exceeds OpenSSL performance while using less memory, with better optimization for modern TLS versions.

Simpler Codebase: rustls has a much smaller, more auditable codebase (~100K lines vs OpenSSL's ~500K+ lines), making security reviews more feasible.

Industry Support: Major companies like Cloudflare, Google, and others actively support and use rustls in production, and there's significant funding from major tech companies supporting rustls development.

For Tari's use case, rustls provides better security guarantees, consistent cross-platform behavior, and avoids potential OpenSSL build complexities while maintaining competitive performance.


🧠 Learnings used
Learnt from: MCozhusheck
PR: tari-project/tari#7478
File: base_layer/p2p/src/peer_seeds.rs:96-104
Timestamp: 2025-09-08T08:50:32.976Z
Learning: MCozhusheck requested to be reminded later to change resolve_download_url() in base_layer/p2p/src/peer_seeds.rs to HTTPS-only (removing HTTP support) once their HTTPS endpoints are available. Currently HTTP support is needed due to infrastructure constraints.

semver = { version = "1.0.1", optional = true }
serde = "1.0.90"
serde_json = "1.0.51"
thiserror = "1.0.26"
tokio = { version = "1.44", features = ["macros"] }
tokio-stream = { version = "0.1.9", default-features = false, features = [
Expand All @@ -52,7 +53,7 @@ tari_common = { workspace = true, features = ["build"] }

[features]
test-mocks = []
auto-update = ["reqwest/default", "pgp", "semver"]
auto-update = ["semver"]

[package.metadata.cargo-machete]
ignored = []
12 changes: 6 additions & 6 deletions base_layer/p2p/src/auto_update/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crate::dns::DnsClientError;
use thiserror::Error;

#[derive(Debug, thiserror::Error)]
use crate::{dns::DnsClientError, signature_verification::SignatureVerificationError};

#[derive(Debug, Error)]
pub enum AutoUpdateError {
#[error("DNS Client error: {0}")]
DnsClientError(#[from] DnsClientError),
#[error("Failed to download file: {0}")]
DownloadError(#[from] reqwest::Error),
#[error("Failed to verify signature: {0}")]
SignatureError(#[from] pgp::errors::Error),
#[error("Signature verification error: {0}")]
SignatureVerificationError(#[from] SignatureVerificationError),
}
62 changes: 14 additions & 48 deletions base_layer/p2p/src/auto_update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

mod dns;
mod signature;

mod service;
pub use service::{SoftwareUpdaterHandle, SoftwareUpdaterService};
Expand All @@ -30,15 +29,12 @@ mod error;
use std::{
fmt,
fmt::{Display, Formatter},
io,
str::FromStr,
time::Duration,
};

pub use error::AutoUpdateError;
use futures::future;
use pgp::Deserializable;
use reqwest::IntoUrl;
// Re-exports of foreign types used in public interface
pub use semver::Version;
use serde::{Deserialize, Serialize};
Expand All @@ -54,7 +50,10 @@ use tari_common::{
};
use tari_utilities::hex::Hex;

use crate::auto_update::{dns::UpdateSpec, signature::SignedMessageVerifier};
use crate::{
auto_update::dns::UpdateSpec,
signature_verification::{self, SignedMessageVerifier},
};

const LOG_TARGET: &str = "p2p::auto_update";

Expand Down Expand Up @@ -122,24 +121,24 @@ pub async fn check_for_updates(

);
let (hashes, sig) = future::join(
download_hashes_file(&hashes_url),
download_hashes_sig_file(&hashes_sig_url),
signature_verification::download_hashes_file(&hashes_url),
signature_verification::download_hashes_sig_file(&hashes_sig_url),
)
.await;
let hashes = hashes?;
let sig = sig?;
let verifier = SignedMessageVerifier::new(maintainers().collect());
verifier
.verify_signed_update(&sig, &hashes, &update_spec)
.map(|(_, filename)| {
let verifier = SignedMessageVerifier::new(signature_verification::maintainers().collect());
match verifier.verify_signed_hashes(&sig, &hashes, &update_spec.hash) {
Ok((_, filename)) => {
let download_url = format!("{download_base_url}/{filename}");
log::info!(target: LOG_TARGET, "Valid update found at {download_url}");
Ok(SoftwareUpdate {
Ok(Some(SoftwareUpdate {
spec: update_spec,
download_url,
})
})
.transpose()
}))
},
Err(_) => Ok(None),
}
},
None => {
log::info!("No new updates for {app} ({arch} {version})");
Expand Down Expand Up @@ -183,45 +182,12 @@ impl Display for SoftwareUpdate {
}
}

async fn download_hashes_file<T: IntoUrl>(url: T) -> Result<String, AutoUpdateError> {
let resp = http_download(url).await?;
let txt = resp.text().await?;
Ok(txt)
}

async fn download_hashes_sig_file<T: IntoUrl>(url: T) -> Result<pgp::StandaloneSignature, AutoUpdateError> {
let resp = http_download(url).await?;
let sig_bytes = resp.bytes().await?;
let cursor = io::Cursor::new(&sig_bytes);
let sig = pgp::StandaloneSignature::from_bytes(cursor).map_err(AutoUpdateError::SignatureError)?;
Ok(sig)
}

async fn http_download<T: IntoUrl>(url: T) -> Result<reqwest::Response, AutoUpdateError> {
let resp = reqwest::get(url).await?.error_for_status()?;
Ok(resp)
}

const MAINTAINERS: &[&str] = &[include_str!("gpg_keys/swvheerden.asc")];

fn maintainers() -> impl Iterator<Item = pgp::SignedPublicKey> {
MAINTAINERS.iter().map(|s| {
let (pk, _) = pgp::SignedPublicKey::from_string(s).expect("Malformed maintainer PGP signature");
pk
})
}

#[cfg(test)]
mod test {
use tari_common::DefaultConfigLoader;

use super::*;

#[test]
fn all_maintainers_well_formed() {
assert_eq!(maintainers().count(), MAINTAINERS.len());
}

fn get_config(config_name: Option<&str>) -> config::Config {
let s = match config_name {
Some(o) => {
Expand Down
157 changes: 0 additions & 157 deletions base_layer/p2p/src/auto_update/signature.rs

This file was deleted.

6 changes: 6 additions & 0 deletions base_layer/p2p/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ pub struct PeerSeedsConfig {
/// All DNS seed records must pass DNSSEC validation
#[serde(default)]
pub dns_seeds_use_dnssec: bool,
#[serde(default)]
pub download_url: String,
}

impl Default for PeerSeedsConfig {
Expand Down Expand Up @@ -98,6 +100,10 @@ impl Default for PeerSeedsConfig {
)
.expect("string is valid"),
dns_seeds_use_dnssec: false,
download_url: format!(
"https://cdn-universe.tari.com/tari-project/tari/{}/seednodes.json",
Network::get_current_or_user_setting_or_default().as_key_str()
),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions base_layer/p2p/src/dns/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ pub enum DnsClientError {
DnsNameRequiredForDnsSec,
#[error("Connection error: {0}")]
Connection(String),
#[error("No download URL found")]
NoDownloadUrlFound,
}
Loading
Loading