Skip to content

Commit 5716902

Browse files
committed
Remove GPG signature support
This is a breaking change: the gpg config settings, variables, and related cli commands are all removed. Fixes: #3250 by removing our GPG support. - the foundation's new security engineer Walter Pearce is working on a new system, not based around GPG, for validation of distributions - we don't rely on the signatures today - these warnings are not errors by default - sustained ignored, unfixable signature errors will teach folk to ignore them, which is harmful to everyone - we could do streaming unpacking (with some changes) if we trust the transport rather than the current monolithic signature validation, which could improve performance Downloads still have a checksum which is verified.
1 parent 9747102 commit 5716902

17 files changed

+67
-1521
lines changed

Cargo.lock

Lines changed: 27 additions & 975 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
[package]
2-
authors = ["Daniel Silverstone <dsilvers@digital-scurf.org>", "Diggory Blake <diggsey@googlemail.com>"]
2+
authors = [
3+
"Daniel Silverstone <dsilvers@digital-scurf.org>",
4+
"Diggory Blake <diggsey@googlemail.com>",
5+
]
36
build = "build.rs"
47
description = "Manage multiple rust installations with ease"
58
edition = "2021"
@@ -13,7 +16,12 @@ version = "1.25.2"
1316

1417
[features]
1518
curl-backend = ["download/curl-backend"]
16-
default = ["curl-backend", "reqwest-backend", "reqwest-default-tls", "reqwest-rustls-tls"]
19+
default = [
20+
"curl-backend",
21+
"reqwest-backend",
22+
"reqwest-default-tls",
23+
"reqwest-rustls-tls",
24+
]
1725

1826
reqwest-backend = ["download/reqwest-backend"]
1927
vendored-openssl = ['openssl/vendored']
@@ -29,9 +37,9 @@ no-self-update = []
2937
anyhow.workspace = true
3038
cfg-if = "1.0"
3139
chrono = "0.4"
32-
clap = {version = "3", features = ["wrap_help"]}
40+
clap = { version = "3", features = ["wrap_help"] }
3341
clap_complete = "3"
34-
download = {path = "download", default-features = false}
42+
download = { path = "download", default-features = false }
3543
effective-limits = "0.5.5"
3644
enum-map = "2.4.2"
3745
flate2 = "1"
@@ -44,16 +52,15 @@ opener = "0.5.2"
4452
# Used by `curl` or `reqwest` backend although it isn't imported by our rustup :
4553
# this allows controlling the vendoring status without exposing the presence of
4654
# the download crate.
47-
openssl = {version = "0.10", optional = true}
48-
pulldown-cmark = {version = "0.9", default-features = false}
55+
openssl = { version = "0.10", optional = true }
56+
pulldown-cmark = { version = "0.9", default-features = false }
4957
rand = "0.8"
5058
regex = "1"
51-
remove_dir_all = {version= "0.8.1", features=["parallel"]}
59+
remove_dir_all = { version = "0.8.1", features = ["parallel"] }
5260
same-file = "1"
5361
scopeguard = "1"
5462
semver = "1.0"
55-
serde = {version = "1.0", features = ["derive"]}
56-
sequoia-openpgp = { version = "1.13", default-features = false, features = ["crypto-rust", "allow-experimental-crypto", "allow-variable-time-crypto"] }
63+
serde = { version = "1.0", features = ["derive"] }
5764
sha2 = "0.10"
5865
sharded-slab = "0.1.1"
5966
strsim = "0.10"
@@ -64,11 +71,11 @@ term = "=0.7.0"
6471
thiserror.workspace = true
6572
threadpool = "1"
6673
toml = "0.5"
74+
trycmd = "0.14.13"
6775
url.workspace = true
6876
wait-timeout = "0.2"
6977
xz2 = "0.1.3"
7078
zstd = "0.12"
71-
trycmd = "0.14.13"
7279

7380
[dependencies.retry]
7481
default-features = false
@@ -85,34 +92,34 @@ winreg = "0.11"
8592

8693
[target."cfg(windows)".dependencies.winapi]
8794
features = [
88-
"combaseapi",
89-
"errhandlingapi",
90-
"fileapi",
91-
"handleapi",
92-
"ioapiset",
93-
"jobapi",
94-
"jobapi2",
95-
"minwindef",
96-
"processthreadsapi",
97-
"psapi",
98-
"shlobj",
99-
"shtypes",
100-
"synchapi",
101-
"sysinfoapi",
102-
"tlhelp32",
103-
"userenv",
104-
"winbase",
105-
"winerror",
106-
"winioctl",
107-
"winnt",
108-
"winuser",
95+
"combaseapi",
96+
"errhandlingapi",
97+
"fileapi",
98+
"handleapi",
99+
"ioapiset",
100+
"jobapi",
101+
"jobapi2",
102+
"minwindef",
103+
"processthreadsapi",
104+
"psapi",
105+
"shlobj",
106+
"shtypes",
107+
"synchapi",
108+
"sysinfoapi",
109+
"tlhelp32",
110+
"userenv",
111+
"winbase",
112+
"winerror",
113+
"winioctl",
114+
"winnt",
115+
"winuser",
109116
]
110117
version = "0.3"
111118

112119
[dev-dependencies]
113-
walkdir = "2"
114-
once_cell = "1.17.1"
115120
enum-map = "2.4.2"
121+
once_cell = "1.17.1"
122+
walkdir = "2"
116123

117124
[build-dependencies]
118125
lazy_static = "1"
@@ -139,4 +146,3 @@ lto = true
139146
# Reduce build time by setting proc-macro crates non optimized.
140147
[profile.release.build-override]
141148
opt-level = 0
142-

src/cli/rustup_mode.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ pub fn main() -> Result<utils::ExitCode> {
135135
("active-toolchain", m) => handle_epipe(show_active_toolchain(cfg, m))?,
136136
("home", _) => handle_epipe(show_rustup_home(cfg))?,
137137
("profile", _) => handle_epipe(show_profile(cfg))?,
138-
("keys", _) => handle_epipe(show_keys(cfg))?,
139138
_ => handle_epipe(show(cfg, c))?,
140139
},
141140
None => handle_epipe(show(cfg, c))?,
@@ -1668,15 +1667,6 @@ fn show_profile(cfg: &Cfg) -> Result<utils::ExitCode> {
16681667
Ok(utils::ExitCode(0))
16691668
}
16701669

1671-
fn show_keys(cfg: &Cfg) -> Result<utils::ExitCode> {
1672-
for key in cfg.get_pgp_keys() {
1673-
for l in key.show_key()? {
1674-
info!("{}", l);
1675-
}
1676-
}
1677-
Ok(utils::ExitCode(0))
1678-
}
1679-
16801670
#[derive(Copy, Clone, Debug, PartialEq)]
16811671
pub(crate) enum CompletionCommand {
16821672
Rustup,

src/config.rs

Lines changed: 0 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@ use std::str::FromStr;
77
use std::sync::Arc;
88

99
use anyhow::{anyhow, bail, Context, Result};
10-
use sequoia_openpgp::{parse::Parse, Cert};
1110
use serde::Deserialize;
1211
use thiserror::Error as ThisError;
1312

1413
use crate::cli::self_update::SelfUpdateMode;
1514
use crate::dist::download::DownloadCfg;
16-
use crate::dist::signatures::sequoia_policy;
1715
use crate::dist::{
1816
dist::{self, Profile},
1917
temp,
@@ -155,77 +153,6 @@ impl<'a> OverrideCfg<'a> {
155153
}
156154
}
157155

158-
lazy_static::lazy_static! {
159-
static ref BUILTIN_PGP_KEY: Cert =
160-
Cert::from_bytes(&include_bytes!("rust-key.pgp.ascii")[..]).unwrap();
161-
}
162-
163-
#[allow(clippy::large_enum_variant)] // Builtin is tiny, the rest are sane
164-
#[derive(Debug)]
165-
pub enum PgpPublicKey {
166-
Builtin,
167-
FromEnvironment(PathBuf, Cert),
168-
FromConfiguration(PathBuf, Cert),
169-
}
170-
171-
impl PgpPublicKey {
172-
/// Retrieve the key.
173-
pub(crate) fn cert(&self) -> &Cert {
174-
match self {
175-
Self::Builtin => &BUILTIN_PGP_KEY,
176-
Self::FromEnvironment(_, k) => k,
177-
Self::FromConfiguration(_, k) => k,
178-
}
179-
}
180-
181-
/// Display the key in detail for the user
182-
pub(crate) fn show_key(&self) -> Result<Vec<String>> {
183-
fn format_hex(bytes: &[u8], separator: &str, every: usize) -> Result<String> {
184-
use std::fmt::Write;
185-
let mut ret = String::new();
186-
let mut wait = every;
187-
for b in bytes.iter() {
188-
if wait == 0 {
189-
ret.push_str(separator);
190-
wait = every;
191-
}
192-
wait -= 1;
193-
write!(ret, "{b:02X}")?;
194-
}
195-
Ok(ret)
196-
}
197-
let mut ret = vec![format!("from {self}")];
198-
let cert = self.cert();
199-
let keyid = format_hex(cert.keyid().as_bytes(), "-", 4)?;
200-
let algo = cert.primary_key().pk_algo();
201-
let fpr = format_hex(cert.fingerprint().as_bytes(), " ", 2)?;
202-
let p = sequoia_policy();
203-
204-
let uid0 = cert
205-
.with_policy(&p, None)?
206-
.primary_userid()
207-
.map(|u| u.userid().to_string())
208-
.unwrap_or_else(|_| "<No User ID>".into());
209-
ret.push(format!(" {algo:?}/{keyid} - {uid0}"));
210-
ret.push(format!(" Fingerprint: {fpr}"));
211-
Ok(ret)
212-
}
213-
}
214-
215-
impl Display for PgpPublicKey {
216-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217-
match self {
218-
Self::Builtin => write!(f, "builtin Rust release key"),
219-
Self::FromEnvironment(p, _) => {
220-
write!(f, "key specified in RUSTUP_PGP_KEY ({})", p.display())
221-
}
222-
Self::FromConfiguration(p, _) => {
223-
write!(f, "key specified in configuration file ({})", p.display())
224-
}
225-
}
226-
}
227-
}
228-
229156
pub(crate) const UNIX_FALLBACK_SETTINGS: &str = "/etc/rustup/settings.toml";
230157

231158
pub struct Cfg {
@@ -237,7 +164,6 @@ pub struct Cfg {
237164
pub update_hash_dir: PathBuf,
238165
pub download_dir: PathBuf,
239166
pub temp_cfg: temp::Cfg,
240-
pgp_keys: Vec<PgpPublicKey>,
241167
pub toolchain_override: Option<String>,
242168
pub env_override: Option<String>,
243169
pub dist_root_url: String,
@@ -271,35 +197,6 @@ impl Cfg {
271197
let update_hash_dir = rustup_dir.join("update-hashes");
272198
let download_dir = rustup_dir.join("downloads");
273199

274-
// PGP keys
275-
let mut pgp_keys: Vec<PgpPublicKey> = vec![PgpPublicKey::Builtin];
276-
277-
if let Some(ref s_path) = process().var_os("RUSTUP_PGP_KEY") {
278-
let path = PathBuf::from(s_path);
279-
let file = utils::open_file("RUSTUP_PGP_KEY", &path)?;
280-
let key = Cert::from_reader(file).map_err(|error| RustupError::InvalidPgpKey {
281-
path: s_path.into(),
282-
source: error,
283-
})?;
284-
285-
pgp_keys.push(PgpPublicKey::FromEnvironment(path, key));
286-
}
287-
settings_file.with(|s| {
288-
if let Some(s) = &s.pgp_keys {
289-
let path = PathBuf::from(s);
290-
let file = utils::open_file("PGP Key from config", &path)?;
291-
let key = Cert::from_reader(file).map_err(|error| {
292-
anyhow!(RustupError::InvalidPgpKey {
293-
path: s.into(),
294-
source: error,
295-
})
296-
})?;
297-
298-
pgp_keys.push(PgpPublicKey::FromConfiguration(path, key));
299-
}
300-
Ok(())
301-
})?;
302-
303200
// Environment override
304201
let env_override = process()
305202
.var("RUSTUP_TOOLCHAIN")
@@ -338,7 +235,6 @@ impl Cfg {
338235
update_hash_dir,
339236
download_dir,
340237
temp_cfg,
341-
pgp_keys,
342238
notify_handler,
343239
toolchain_override: None,
344240
env_override,
@@ -364,14 +260,9 @@ impl Cfg {
364260
temp_cfg: &self.temp_cfg,
365261
download_dir: &self.download_dir,
366262
notify_handler,
367-
pgp_keys: self.get_pgp_keys(),
368263
}
369264
}
370265

371-
pub(crate) fn get_pgp_keys(&self) -> &[PgpPublicKey] {
372-
&self.pgp_keys
373-
}
374-
375266
pub(crate) fn set_profile_override(&mut self, profile: dist::Profile) {
376267
self.profile_override = Some(profile);
377268
}

src/dist/dist.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,6 @@ fn try_update_from_dist_(
997997
update_hash,
998998
download.temp_cfg,
999999
&download.notify_handler,
1000-
download.pgp_keys,
10011000
);
10021001
// inspect, determine what context to add, then process afterwards.
10031002
let mut download_not_exists = false;

src/dist/download.rs

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use anyhow::{anyhow, Context, Result};
66
use sha2::{Digest, Sha256};
77
use url::Url;
88

9-
use crate::config::PgpPublicKey;
109
use crate::dist::notifications::*;
1110
use crate::dist::temp;
1211
use crate::errors::*;
@@ -20,7 +19,6 @@ pub struct DownloadCfg<'a> {
2019
pub temp_cfg: &'a temp::Cfg,
2120
pub download_dir: &'a PathBuf,
2221
pub notify_handler: &'a dyn Fn(Notification<'_>),
23-
pub pgp_keys: &'a [PgpPublicKey],
2422
}
2523

2624
pub(crate) struct File {
@@ -137,43 +135,6 @@ impl<'a> DownloadCfg<'a> {
137135
utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned())
138136
}
139137

140-
fn download_signature(&self, url: &str) -> Result<String> {
141-
let sig_url = utils::parse_url(&(url.to_owned() + ".asc"))?;
142-
let sig_file = self.temp_cfg.new_file()?;
143-
144-
utils::download_file(&sig_url, &sig_file, None, &|n| {
145-
(self.notify_handler)(n.into())
146-
})?;
147-
148-
utils::read_file("signature", &sig_file)
149-
}
150-
151-
fn check_signature(&self, url: &str, file: &temp::File<'_>) -> Result<&PgpPublicKey> {
152-
assert!(
153-
!self.pgp_keys.is_empty(),
154-
"At least the builtin key must be present"
155-
);
156-
157-
let signature = self
158-
.download_signature(url)
159-
.with_context(|| format!("failed to download signature file {url}"))?;
160-
161-
let file_path: &Path = file;
162-
let content = std::fs::File::open(file_path).with_context(|| RustupError::ReadingFile {
163-
name: "channel data",
164-
path: PathBuf::from(file_path),
165-
})?;
166-
167-
let sig_result =
168-
crate::dist::signatures::verify_signature(content, &signature, self.pgp_keys)?;
169-
if let Some(keyidx) = sig_result {
170-
let key = &self.pgp_keys[keyidx];
171-
Ok(key)
172-
} else {
173-
Err(anyhow!(format!("signature verification failed for {url}")))
174-
}
175-
}
176-
177138
/// Downloads a file, sourcing its hash from the same url with a `.sha256` suffix.
178139
/// If `update_hash` is present, then that will be compared to the downloaded hash,
179140
/// and if they match, the download is skipped.
@@ -224,14 +185,6 @@ impl<'a> DownloadCfg<'a> {
224185
(self.notify_handler)(Notification::ChecksumValid(url_str));
225186
}
226187

227-
// No signatures for tarballs for now.
228-
if !url_str.ends_with(".tar.gz") && !url_str.ends_with(".tar.xz") {
229-
match self.check_signature(url_str, &file) {
230-
Ok(key) => (self.notify_handler)(Notification::SignatureValid(url_str, key)),
231-
Err(_) => (self.notify_handler)(Notification::SignatureInvalid(url_str)),
232-
}
233-
}
234-
235188
Ok(Some((file, partial_hash)))
236189
}
237190
}

0 commit comments

Comments
 (0)