Open g2p opened 1 year ago
Working on a fix.
Thanks for investigating this! I opened a PR that should suppress the warning, hopefully we'll be able to release a new Rustup version with that today. https://github.com/rust-lang/rustup/pull/3186
Thanks for being so on top of this, @pietroalbini!
Do we need to do more work here (e.g. switch to a stronger hash in the infra channels, and resign older releases?)
As I understand it SHA1 is used as part of signing subkeys of the root signing key. It isn't used as part of signing the actual release tarballs.
Do we need to do more work here (e.g. switch to a stronger hash in the infra channels, and resign older releases?)
It would be good to update the signing certificate so that it is valid without SHA-1. This can be done with e.g. sq-keyring-linter --fix
.
Ideally, rustup would also opportunistically download the certificate in order to get updates, e.g., from https://rustup.rs/root.pgp
and from https://keys.openpgp.org. Since the certificate is pinned in rustup, an attacker may be able to withhold updates (although this is mitigate partially by checking for updates from multiple locations), but they won't be able to trick the user into using the wrong certificate.
Be careful when refreshing update keys out of band, this should only update existing keys but reject any new subkeys to not break future update transparency efforts.
Be careful when refreshing update keys out of band, this should only update existing keys but reject any new subkeys to not break future update transparency efforts.
Why should new subkeys be rejected?
The rust release key rust-key@rust-lang.org
is currently setup like this (although the first one is technically not a subkey):
108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE Rust Language (Tag and Release Signing Key) <rust-key@rust-lang.org>
108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE Subkey 108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE
108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE Subkey C13466B7E169A085188632165CB4A9347B3B09DC
108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE Subkey 29D57CE15D76E78CD57D326A8E9AA3F7AB3F5826
Or in gpg output:
% gpg --fingerprint --fingerprint --list-keys 108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE
pub rsa4096 2013-09-26 [SC]
108F 6620 5EAE B0AA A8DD 5E1C 85AB 96E6 FA1B E5FE
uid [ unknown] Rust Language (Tag and Release Signing Key) <rust-key@rust-lang.org>
sub rsa4096 2013-09-26 [E]
29D5 7CE1 5D76 E78C D57D 326A 8E9A A3F7 AB3F 5826
sub rsa4096 2014-12-15 [S]
C134 66B7 E169 A085 1886 3216 5CB4 A934 7B3B 09DC
This means the following keys can issue signatures for updates and need to be monitored (I guess the encryption-only key can be ignored):
108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE
C13466B7E169A085188632165CB4A9347B3B09DC
This is currently hard-coded into the rustup binary, as long as I can ensure the user has a legitimate copy of rustup I can be sure it's going to attempt to enforce this policy.
Now if we allow subkey updates (essentially "if we allow delegation of trust"), somebody with access to 108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE
could issue a new subkey (we're going to refer to it as 1337133713371337133713371337133713371337
) that also has the signing capability enabled. If the person we're targeting downloads this key (or especially if it's going to saved to ~/.rustup
), their new set of trusted keys that can issue signatures for updates looks like this:
108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE
C13466B7E169A085188632165CB4A9347B3B09DC
1337133713371337133713371337133713371337
But because you don't know about this secret subkey you're still monitoring the transparency log for these two keys:
108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE
C13466B7E169A085188632165CB4A9347B3B09DC
This means I could safely submit my malicious update (signed by 1337133713371337133713371337133713371337
) to the transparency log and get inclusion proof without setting of any alarms. Even if the signature gets discovered during the attack it would be very difficult to detect the key compromise itself because you can't know if the signature from 1337133713371337133713371337133713371337
has any trust delegated to it unless you also know the content of ~/.rustup
of the user I'm targeting.
Thanks for your write up. What transparency log are you talking about? I wasn't aware that rustup was using that.
(This went slightly off-topic, feel free to skip to the next section):
rustup itself isn't using any transparency log, there are two projects attempting to do this for pgp-in-general though:
Searching for the public rekor log currently yields one entry for the rust release key (src/rust-key.pgp.ascii
is located in the rustup repository):
% rekor-cli search --public-key src/rust-key.pgp.ascii
Found matching entries (listed by UUID):
24296fb24b8ad77a49f171daa9a8b39041e6b36ec74785714cead6b07b7293d910440c751875550d
Inspecting the entry shows it's a signature over the sha256 hash 642f09caa48e97de84958f8d4da5160ca298b94bf5360ba12c15be72877d487a
:
We don't know what this data was though, the rust project would need to keep a public archive of all data they've signed to allow auditing. If I recall correctly this would be an archive of all release-stable.toml, these then contain a sha256sum of the other artifacts, so if you have a complete view of all release-stable.toml you also have a complete view of all compilers that have been distributed.
Anyway regarding the original issue and since I looked into the keys anyway, this is the output of sq-keyring-linter
:
% sq-keyring-linter --fix rust-key.pgp.ascii
Certificate 85AB96E6FA1BE5FE is not valid under the standard policy: No binding signature at time 2023-02-15T17:23:07Z
Certificate 85AB96E6FA1BE5FE contains a User ID ("Rust Language (Tag and Release Signing Key) <rust-key@rust-lang.org>") protected by SHA-1
Certificate 85AB96E6FA1BE5FE: User ID Rust Language (Tag and Release Signing Key) <rust-key@rust-lang.org>: Failed to update binding signature: Invalid argument: No secret key
Certificate 85AB96E6FA1BE5FE, key 5CB4A9347B3B09DC uses a SHA-1-protected binding signature.
Certificate 85AB96E6FA1BE5FE, key 5CB4A9347B3B09DC: Failed to update binding signature: Invalid argument: No secret key
Certificate 85AB96E6FA1BE5FE, key 8E9AA3F7AB3F5826 uses a SHA-1-protected binding signature.
Certificate 85AB96E6FA1BE5FE, key 8E9AA3F7AB3F5826: Failed to update binding signature: Invalid argument: No secret key
Examined 1 certificate.
0 certificates are invalid and were not linted. (GOOD)
1 certificate was linted.
1 of the 1 certificates (100%) has at least one issue. (BAD)
0 of the linted certificates were revoked.
0 of the 0 certificates has revocation certificates that are weaker than the certificate and should be recreated. (GOOD)
0 of the linted certificates were expired.
1 of the non-revoked linted certificate has at least one non-revoked User ID:
1 has at least one User ID protected by SHA-1. (BAD)
1 has all User IDs protected by SHA-1. (BAD)
1 of the non-revoked linted certificates has at least one non-revoked, live subkey:
1 has at least one non-revoked, live subkey with a binding signature that uses SHA-1. (BAD)
0 of the non-revoked linted certificates have at least one non-revoked, live, signing-capable subkey:
0 certificates have at least one non-revoked, live, signing-capable subkey with a strong binding signature, but a backsig that uses SHA-1. (GOOD)
Failed to fix 3 issues. (BAD)
Basically the primary key 108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE
self-certifies the uid Rust Language (Tag and Release Signing Key) <rust-key@rust-lang.org>
and both subkeys belong to 108F66205EAEB0AAA8DD5E1C85AB96E6FA1BE5FE
but it does so with sha1:
To fix this with sequoia, use:
$ apt-get install sq-keyring-linter
$ gpg --export-secret-keys rust-key@rust-lang.org | sq-keyring-linter --fix | gpg --import
gpg is going to ask for as password to protect the secret key, but sq-keyring-linter is going to immediately ask for the password again to issue new sha2 signatures for the uid paket and both subkeys, so it doesn't really matter what you set there.
I've tried to find instructions on how to fix the key with gpg natively but it seems very involved (https://old.nixaid.com/gpg-migration-sha1-to-sha2/). It also doesn't explain how to get rid of the subkey signatures and suggests to revoke them and generate new ones (?!).
Cross-referecing the release scripts, this should work:
(
for x in "op://tnuk54f56audltg5e7if776ieq/67bxwduvibdkpio7psx53preka/56b6j26lmvelpn6jeecsr5fsxi" "op://tnuk54f56audltg5e7if776ieq/q6ztr2lrorg3fa3mzg6kx26bnm/m6tnrye775cefbekajhawec3ja" "op://tnuk54f56audltg5e7if776ieq/fnzoe3nw7rck7ezqhwpkj7j2xe/kjzmpsermnc4bovkwslhjd34se"; do
op read "$x"
done
) | sq-keyring-linter --fix -p "$(op read op://tnuk54f56audltg5e7if776ieq/pks4bn2wf4r7plue24cdqsw4ga/password)" | gpg --import
# in rustup codebase
gpg --armor --export rust-key@rust-lang.org > src/rust-key.pgp.ascii
If you just want to go with a new key and you want to go with gpg again, put this in your ~/.gnupg/gpg.conf
first:
personal-cipher-preferences AES256
personal-digest-preferences SHA512
cert-digest-algo SHA512
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
Sorry this reply got so long, if anything is unclear please just let me know.
I had assumed we'd update the embedde certificate in rustup via new rustup release rather than sideloading keys. We need to do that in some form of expand-contract so that rustup's self-update continues to work across the transition.
I reverse engineered the rustup self-update recently and the pgp signatures aren't fully in use yet.
rustup fetches this url: https://static.rust-lang.org/rustup/release-stable.toml
schema-version = '1'
version = '1.25.2'
If this version is less recent than rustup's own version (say 1.25.1) it's going to fetch and execute the elf file from this url next: https://static.rust-lang.org/rustup/archive/1.25.2/x86_64-unknown-linux-musl/rustup-init
.
Security is provided by https and pgp signatures aren't mandatory yet, so rolling out the update shouldn't cause issues.
I have unit-tests for rustup here: https://github.com/kpcyrd/sh4d0wup/blob/main/contrib/plot-rustup.yaml
Let's not bikeshed a key rotation mechanism in this thread, but let's focus on removing the SHA1 signatures from the existing key. Key rotation (and in general enforcing signatures) would require a larger design effort, and I know it's one of the priorities of the foundation security engineer.
Problem
As described here: https://www.reddit.com/r/rust/comments/10qlf1q/nightly_dc1d9d50f_20230131_signature_verification/
rustup update and rustup check report that https://static.rust-lang.org/dist/channel-rust-nightly.toml / https://static.rust-lang.org/dist/channel-rust-nightly.toml.asc aren't valid anymore.
Steps
Manually
sqv is from
cargo install sequoia-sqv
.Possible Solution(s)
This should be fixed outside rustup, but I don't know where to file a report on the release process. Please switch away from using SHA1 in keys or signatures.
Notes
No response
Rustup version
Installed toolchains