Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
alvinhochun opened this issue Dec 11, 2018 · 35 comments
Open

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

alvinhochun opened this issue Dec 11, 2018 · 35 comments

Comments

@alvinhochun
Copy link

alvinhochun commented Dec 11, 2018

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 #1568 (comment) 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
Copy link

eddyp commented Dec 24, 2018

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

@aidanhs
Copy link
Member

aidanhs commented Jan 1, 2019

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

  • signing rustup is something that's come up before and the only reason we don't do it is we haven't got round to it
  • we're open to exploring/moving forward on signing, but we can't commit to anything before understanding fully what's involved
  • on the subject of understanding - we'd welcome help here! It'd be great to get an overview of the steps involved, best practices and so on to be able to see what the next steps would be if we did want to move forward

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

@eddyp
Copy link

eddyp commented Jan 1, 2019

@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
Copy link
Contributor

jethrogb commented Jan 2, 2019

@jseyfried can you share Windows binary signing best practices?

@alvinhochun
Copy link
Author

@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
Copy link

eddyp commented Jan 3, 2019

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
Copy link

eddyp commented Jan 4, 2019

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
Copy link
Contributor

jethrogb commented Jan 8, 2019

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
Copy link
Contributor

kornelski commented Jan 17, 2019

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
Copy link

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
Copy link
Contributor

jethrogb commented Jan 18, 2019

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

@ddevienne
Copy link

ddevienne commented Jun 6, 2019

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
Copy link
Contributor

kinnison commented Jun 6, 2019

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
Copy link
Contributor

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
Copy link

ddevienne commented Jun 25, 2019

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
Copy link
Contributor

@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
Copy link
Contributor

jethrogb commented Jul 9, 2019

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

Mozilla has basically agreed to let us use their code-signing certificate & infrastructure. 🎉 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:

  • Encapsulate the code below in a "autograph client library" that can be called from C
  • Create an OpenSSL engine using the C API that uses the autograph client library to do RSA signing
  • Modify osslsigncode to use our custom OpenSSL engine
  • Create signing microservice that can be pinged from CI to handle signing requests
  • Integrate everything in CI with a test instance of the microservice & autograph
  • Once this is all working, ping @Manishearth to liase with relevant people at Mozilla for production setup

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

This comment has been minimized.

@rustbot

This comment has been minimized.

@rtrusso
Copy link

rtrusso commented Sep 7, 2019

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
Copy link
Contributor

kinnison commented Sep 8, 2019

@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
Copy link

1600 commented Sep 13, 2019

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

@kinnison
Copy link
Contributor

@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
Copy link
Contributor

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

@CIPop
Copy link

CIPop commented Dec 1, 2020

@workingjubilee
Copy link
Member

@rustbot label: +O-windows

@rustbot rustbot added the O-windows Windows related label Apr 29, 2021
@davidanthoff
Copy link

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
Copy link
Contributor

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

@kinnison
Copy link
Contributor

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
Copy link

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

This comment was marked as abuse.

@rbtcollins
Copy link
Contributor

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
Copy link

asesh commented Apr 12, 2024

@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
Copy link
Contributor

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
Copy link

asesh commented Apr 17, 2024

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.

@rami3l rami3l added this to the On Deck milestone Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests