rust-lang / rustup

The Rust toolchain installer
https://rust-lang.github.io/rustup/
Apache License 2.0
6.19k stars 893 forks source link

rustup-init.exe needs to be code-signed #1568

Open alvinhochun opened 5 years ago

alvinhochun commented 5 years ago

NOTE: Edited by @kinnison

Mozilla are prepared to offer access to their Autograph signing service. Thus if you want to help, the relevant useful comment is https://github.com/rust-lang/rustup.rs/issues/1568#issuecomment-509503964 which will guide you through what needs to be done. @jethrogb has indicated they are prepared to mentor this process.


Original posting comes below...


rustup-init.exe (downloaded from https://win.rustup.rs/) is not signed with an EV code signing certificate. As a result, there isn't an obvious way for Windows users to verify the download. (Neither https://www.rust-lang.org/tools/install nor https://rustup.rs/ shows any gpg keys or even checksums for use.)

Moreover, the rustup-init.exe can get blocked by SmartScreen. Even though users can bypass it by clicking on "More Info" then "Run anyway", it doesn't bring any confidence.

SmartScreen operates on the basis of reputation. The SmartScreen warning might eventually go away when the rustup-init.exe gets used by more users, but it resets whenever a new version is built and published. If rustup-init.exe is code-signed, the reputation will be inherited by any programs signed with the same certificate, which should make SmartScreen happy. (See: https://blogs.msdn.microsoft.com/ie/2012/08/14/microsoft-smartscreen-extended-validation-ev-code-signing-certificates/)

eddyp commented 5 years ago

I agree, all official releases should be signed, ideally with keys which have a good chain of trust.

aidanhs commented 5 years ago

We discussed this in an infra team meeting. In summary:

(other open questions involve things like: What about OSX signing? Does the approach we choose for rustup generalise to other binaries we distribute?)

eddyp commented 5 years ago

@aidanhs I have no experience with signing of Windows binaries (I have with signing packages and repositories for Debian based Linux distros), but I expect the approach to take on one binary to work on the all others on that same platform. Typically the platform specific docs should provide exact details, and automation should be possible for all, but the exact details will probably be different.

BTW, if individual binary/file signing is not possible, the second best thing is to sign the packaging format and files with checksums for each file. This is a good idea no matter what to make sure tool chains are not corrupted, infected or modified and some commands could allow verification.

jethrogb commented 5 years ago

@jseyfried can you share Windows binary signing best practices?

alvinhochun commented 5 years ago

@aidanhs

I have some slight idea on how it works. You get a code signing cert and you would invoke signtool.exe from the Windows SDK somewhere in the build/packaging process to sign the output executables (.exe/.dll).

Some interest points I can think of:

If all you care is for the initial SmartScreen warning on rustup-init.exe to not appear every time a new one is released, it should be enough to only sign that and leave the installed files (e.g. rustc.exe/rust-lld.exe/rls.exe) unsigned (see note below). But since you would already have the code signing cert, there is little reason to not sign them all unless it's too tricky to implement for the Rust build infrastructure. (Consider that the keys need to be stored securely.)

To have rustup verify downloaded files would be a separate issue. though it should be possible to leverage the same Windows signing infrastructure for this purpose on Windows. The obvious downside is that it would only work on Windows unless there are cross-platform libraries and tools to support it.


Note: When most (if not all) browsers on Windows download a file, they add an NTFS alternate data stream Zone.Identifier to the file as a flag to mark it as "being from an untrusted zone". This practice started on Internet Explorer on Windows XP and later followed by other browsers. What I believe SmartScreen does is that it scans only the files that have the specific flag, and if the reputation passes the threshold, it would automatically remove the flag and skip the warning. If the file is not flagged in the first place (which is the case for files downloaded by rustup) SmartScreen shouldn't do anything at all.

eddyp commented 5 years ago

Regarding secure storage of the keys, I think a Yubikey device or something similar can do this. For instance, at EuroBSD 2018 the gift package contained a microcontroller based key storage device. The device was sponsored by Modirum and is using the Gnuk OpenPGP firmware from the Free Software Initiative of Japan and it looks like a USB stick.

eddyp commented 5 years ago

An update on the secure key storage, on the Gnuk project page there is a list of compatible devices, and one of them, Nitrokey Start, looks quite cheap (29€) and looks nice, comes pre-installed with all needed SW, and it seems the company has very big names as clients: Google, BBC, SuSE, Redhat, Mozilla, Nvidia, ABB, Adobe... (link to pdf).

jethrogb commented 5 years ago

How would a USB dongle be connected to the CI infrastructure? It seems to me a cloud-based HSMaaS solution such as SDKMS would be more convenient. We can provide a free account for the Rust project, someone from the infra team please contact me to set this up.

kornelski commented 5 years ago

Be careful about types of hardware keys and their software. I've got one that on every signing pops up a GUI that asks for a PIN. There's no CLI version. It doesn't work over SSH and gets in the way of build automation.

notriddle commented 5 years ago

signtool.exe is a PITA; I know, I've tried it.

The easy way to do it is osslsigncode, which can run under any platform that can run basic C code and OpenSSL.

jethrogb commented 5 years ago

Isn't this simply a matter of trying to access the machine keystore when you should be using the user keystore?

ddevienne commented 5 years ago

I'm running Symantec (on Win7), and it too prevents me from running the downloaded exe, because it's unsigned. The file is even deleted from the Downloads folder... I assume Firefox is signed, so I don't see why RustUp can't be...

Notwithstanding the above, how's one supposed to get Rust up-and-running on Windows given this??? Got the O'Reilly Rust book, and if instructions from page 7 don't work, that's not a great start.

Not caring about Windows users for Rust?

Note that this is on a corporate computer, which means I can't control or disable Symantec. I have to confess that for a project hitting 1.0 four years ago, I'm rather surprised with this issue.

UPDATE: Temporarily disabled Symantec with help from admin, so could run downloaded installer. BUT Symantec re-enables itself shortly after, and intercepts attempts to run the installed cargo, rustc, etc... executables (also unsigned obviously, although if coming from signed installer might be OK), deleting them from disk (like the -init.exe installer). I conclude that RUST as a language is not ready to be used by professional developers working on Windows in Corporate environments with mandatory AV software, despite it being mature (4 years since 1.0 release). I guess I'll have to stick to C++ :). Note that by contrast, I had zero-issues using GoLang in that same environment.

kinnison commented 5 years ago

We do care we care greatly. Sadly while we have someone volunteering to help us with sorting out code-signing, it is a complex logistical problem because there's not (yet) a mechanism in place for the project to hold a legal entity to be identified by the certificates. Many people manage to use Rust on Windows, even people with Symantec, Avast, McAffee, and other AVs, so it must be possible. Sadly I'm not the right person to tell you how to do it :(

rbtcollins commented 5 years ago

I think this ticket needs to get escalated out of just our team and into a broader cross-team space - basically the artifacts in the channels are the problem, of which rustup.exe is a special case.

ddevienne commented 5 years ago

Ideally cargo itself would allow easy code signing, which implies support for signing in the tools it forks. Should be as simple as updating the Cargo.toml, no?

jethrogb commented 5 years ago

@ddevienne the technical part of doing the signing is not the issue. As @kinnison mentioned, the big hurdle now is obtaining a trusted (by MS) code signing certificate.

jethrogb commented 5 years ago

I have an update. The following has been the status since early April, but unfortunately none of this was communicated to us until today :confused:

Mozilla has basically agreed to let us use their code-signing certificate & infrastructure. :tada: There's a KMS that Mozilla runs called autograph that's used for signing. They don't want to give Rust CI direct access to this service, so a little proxy utility is needed on Mozilla's taskcluster. When CI needs something signed, it will ping that proxy service with a link to the binary, then the utility will contact autograph to get it signed.

Autograph doesn't have native support for PE signing, but we can use the Generic RSA signing API to get regular PKCS#1 v1.5 signing. The following is needed to complete the flow:

I don't have time to actively work on this, but happy to help mentor someone on this issue (Windows dev environment not required).


This shows how to interact with autograph over the network to get an RSA SHA1 PKCS#1 v1.5 signature. It needs to be modified to use the Generic RSA signing API.

/* Cargo dependencies:
hawk = { version = "3", default-features = false, features = ["use_openssl"] }
hyper = "0.10"
serde = { version = "1", features = ["derive"] }
serde_repr = "0.1"
serde_json = "1"
*/

use serde::{Serialize, Deserialize};
use serde_repr::Serialize_repr;
use hawk::{RequestBuilder, Credentials, Key, SHA256, PayloadHasher};
use hyper::{Client, status::StatusCode, Url, header::{Authorization, ContentType}};

#[derive(Serialize)]
// from https://github.com/mozilla-services/autograph/tree/master/signer/mar#signature-request
struct SignInput<'a> {
    input: &'a str,
    keyid: &'a str,
    options: SignOptions,
}

#[derive(Serialize)]
struct SignOptions {
    sigalg: MarSigAlg
}

#[derive(Serialize_repr)]
#[repr(u8)]
// from https://godoc.org/go.mozilla.org/mar#pkg-constants
enum MarSigAlg {
    RsaPkcs1Sha1 = 1,
    RsaPkcs1Sha384 = 2,
    EcdsaP256Sha256 = 3,
    EcdsaP384Sha384 = 4,
}

#[derive(Deserialize)]
struct SignOutput {
    signature: String,
}

fn sign(url: &str, keyid: &str, input: &str, userid: &str, userkey: &str) -> String {
    let body = serde_json::to_vec(&[SignInput {
        input, keyid, options: SignOptions { sigalg: MarSigAlg::RsaPkcs1Sha1 }
    }]).unwrap();

    let url = Url::parse(url).unwrap().join("sign/hash").unwrap();

    let credentials = Credentials {
        id: userid.to_string(),
        key: Key::new(userkey, SHA256).unwrap(),
    };

    let payload_hash = PayloadHasher::hash("application/json", SHA256, &body).unwrap();
     let request = RequestBuilder::new("POST", url.host_str().unwrap(), url.port().unwrap(), url.path())
        .hash(&payload_hash[..])
        .request();
    let hawk_header = request.make_header(&credentials).unwrap();

    let client = Client::new();
    let res = client.post(url)
        .body(&body[..])
        .header(ContentType::json())
        .header(Authorization(format!("Hawk {}", hawk_header)))
        .send()
        .unwrap();
    assert_eq!(res.status, StatusCode::Created);
    let res: Vec<SignOutput> = serde_json::from_reader(res).unwrap();
    res.into_iter().next().unwrap().signature
}

fn main() {
    let input_sha1hash = "3JBQjLq8/8b9ezhFDLTjya/rt74=";
    // default key & credentials from autograph dev docker container
    println!("{}", sign("http://172.17.0.2:8000", "testmar", input_sha1hash, "alice", "fs5wgcer9qj819kfptdlp8gm227ewxnzvsuj9ztycsx08hfhzu"));
}
jethrogb commented 5 years ago

@rustbot modify labels: E-mentor enhancement help wanted

rustbot commented 5 years ago

Error: This repository is not enabled to use triagebot. Add a triagebot.toml in the root of the master branch to enable it.

Please let @rust-lang/release know if you're having trouble with this bot.

rtrusso commented 5 years ago

I'm new to rust and I'm downloading rustup-init.exe for the first time. While we wait for this issue to get fixed, I would recommend please at least publish a SHA256 or SHA512 hash of the file on the website that we can verify. certutil.exe -hashfile rustup-init.exe sha256 You might also consider using gpg to provide some kind of signature, Windows users should have a gpg binary as part of the git install.

kinnison commented 5 years ago

@rtrusso Next to each binary of rustup-init there is an associated SHA256 checksum available. This is the same URL, with .sha256 added and is used by rustup to validate downloads when it makes them too. Eventually we will be adding support for PGP signature verification but we'll be doing it in-process with Sequoia-PGP.

1600 commented 5 years ago

@kinnison can you please show us URL for the .sha256 checksum file of rustup-init.exe?

kinnison commented 5 years ago

@kinnison can you please show us URL for the .sha256 checksum file of rustup-init.exe?

Sure, the URLs are of the form:

https://static.rust-lang.org/rustup/archive/$VERSION/$TRIPLET/rustup-init$EXE$SUFFIX

Where $VERSION is something like 1.19.0 and $TRIPLET is x86_64-pc-windows-msvc and $EXE is .exe and $SUFFIX is empty for the binary itself, and .sha256 for the shasum.

For the current release, for x86_64-pc-windows-msvc that would therefore be:

https://static.rust-lang.org/rustup/archive/1.19.0/x86_64-pc-windows-msvc/rustup-init.exe

And the corresponding checksum is

https://static.rust-lang.org/rustup/archive/1.19.0/x86_64-pc-windows-msvc/rustup-init.exe.sha256

jethrogb commented 5 years ago

Autograph now has direct support for PKCS v1.5 signing, I updated my status comment above accordingly.

CIPop commented 3 years ago

Similar to https://github.com/rust-lang/rust/issues/67472, Cynet is also flagging this as malware: https://www.virustotal.com/gui/file/a586cf9de3e4aa791fd5796b6a5f99ca05591ccef8bb94e53af5b69f0261fb03/detection

image

workingjubilee commented 3 years ago

@rustbot label: +O-windows

davidanthoff commented 2 years ago

Another option is to use Azure Key Vault for the signing, that might overall be the easiest solution, I think. One can then use https://github.com/vcsjones/AzureSignTool to integrate all of that into a CI workflow.

I think roughly the steps would be:

jethrogb commented 2 years ago

We are still willing to sponsor free signing key storage in Fortanix DSM.

kinnison commented 2 years ago

We're still waiting for the foundation to reach a point that infra can deal with setting up key storage etc. @jethrogb 's offer is definitely in their notes for when we're ready.

BlackHoleFox commented 2 years ago

Sounds like this is gonna get more noticeable in future Win11 updates thanks to Smart App Control which requires some code signature on the binary.

Niproblema commented 1 year ago

5 years later, still havent managed to sign the executable. Suprised you managed to setup https

rbtcollins commented 1 year ago

FWIW Microsoft already white-list rustup.exe in their behavioural list shortly after each release. Being signed is probably better, but most developers should not be experiencing warning dialogues at this point.

asesh commented 7 months ago

@rbtcollins That's for Windows. What about macOS? Code signing files ensure the file came from publisher and hasn't been modified thereafter. We can verify the integrity of files too. It's really necessary from a security POV. Not all OSs are about Windows only!!

rbtcollins commented 7 months ago

Fundamentally someone needs to work with rust's infra team to perform signing of release builds, and if code changes are needed to work well with that, to make those changes too.

asesh commented 7 months ago

Fundamentally someone needs to work with rust's infra team to perform signing of release builds, and if code changes are needed to work well with that, to make those changes too.

This thread is a few years old, I hope someone really takes it seriously and implements it in the CI.