rustup icon indicating copy to clipboard operation
rustup copied to clipboard

rustup-init.exe needs to be code-signed

Open alvinhochun opened this issue 6 years ago • 35 comments

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/)

alvinhochun avatar Dec 11 '18 08:12 alvinhochun

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

eddyp avatar Dec 24 '18 19:12 eddyp

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?)

aidanhs avatar Jan 01 '19 18:01 aidanhs

@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.

eddyp avatar Jan 01 '19 19:01 eddyp

@jseyfried can you share Windows binary signing best practices?

jethrogb avatar Jan 02 '19 04:01 jethrogb

@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:

  • Someone will need to purchase the cert
  • There are different types of code signing cert, some of which require using a hardware token (examples: https://www.digicert.com/code-signing/ev-code-signing-compared.htm)
  • The signing should be time stamped so that the signed executables will still verify after the cert is expired

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.

alvinhochun avatar Jan 03 '19 11:01 alvinhochun

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 avatar Jan 03 '19 18:01 eddyp

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).

eddyp avatar Jan 04 '19 23:01 eddyp

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.

jethrogb avatar Jan 08 '19 04:01 jethrogb

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.

kornelski avatar Jan 17 '19 23:01 kornelski

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

  • It doesn't run in Travis CI for Windows: https://travis-ci.community/t/codesigning-on-windows/1385

  • Even when it works, it requires special permissions: https://support.comodo.com/index.php?/Knowledgebase/Article/View/1187/38/signing-microsoft-windows-user-mode-drivers

  • It only runs on Windows.

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

notriddle avatar Jan 18 '19 00:01 notriddle

  • It doesn't run in Travis CI for Windows: https://travis-ci.community/t/codesigning-on-windows/1385

  • Even when it works, it requires special permissions: https://support.comodo.com/index.php?/Knowledgebase/Article/View/1187/38/signing-microsoft-windows-user-mode-drivers

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

jethrogb avatar Jan 18 '19 07:01 jethrogb

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.

ddevienne avatar Jun 06 '19 08:06 ddevienne

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 :(

kinnison avatar Jun 06 '19 13:06 kinnison

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.

rbtcollins avatar Jun 25 '19 08:06 rbtcollins

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?

ddevienne avatar Jun 25 '19 08:06 ddevienne

@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 avatar Jun 25 '19 14:06 jethrogb

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:

  • [ ] 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 avatar Jul 09 '19 06:07 jethrogb

@rustbot modify labels: E-mentor enhancement help wanted

jethrogb avatar Jul 09 '19 06:07 jethrogb

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.

rustbot avatar Jul 09 '19 06:07 rustbot

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.

rtrusso avatar Sep 07 '19 19:09 rtrusso

@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.

kinnison avatar Sep 08 '19 07:09 kinnison

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

1600 avatar Sep 13 '19 14:09 1600

@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

kinnison avatar Sep 13 '19 14:09 kinnison

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

jethrogb avatar Sep 24 '19 15:09 jethrogb

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

CIPop avatar Dec 01 '20 23:12 CIPop

@rustbot label: +O-windows

workingjubilee avatar Apr 29 '21 21:04 workingjubilee

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:

  • Someone from the Rust Foundation acquires a code signing certificate in the name of the Rust Foundation. I've got mine from https://www.ksoftware.net/code-signing-certificates/, but there are many other vendors as well. I would probably go for the OV version, I think that is less hassle than the EV version and probably is good enough.
  • The Rust Foundation sets up an Azure Subscription and adds the Azure Key Vault service (https://azure.microsoft.com/en-us/pricing/details/key-vault/). That is not free, but the prices seem so low that it seems to me not a problem.
  • The certificate gets uploaded into the key vault.
  • One uses https://github.com/vcsjones/AzureSignTool and the instructions there to integrate the signing into the build process.

davidanthoff avatar Jan 19 '22 06:01 davidanthoff

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

jethrogb avatar Jan 19 '22 07:01 jethrogb

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.

kinnison avatar Feb 13 '22 10:02 kinnison

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.

BlackHoleFox avatar Apr 07 '22 23:04 BlackHoleFox