Open alvinhochun opened 5 years ago
I agree, all official releases should be signed, ideally with keys which have a good chain of trust.
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?)
@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.
@jseyfried can you share Windows binary signing best practices?
@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.
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.
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).
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.
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.
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.
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?
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.
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 :(
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.
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 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.
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"));
}
@rustbot modify labels: E-mentor enhancement help wanted
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.
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 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 can you please show us URL for the .sha256 checksum file of rustup-init.exe?
@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
Autograph now has direct support for PKCS v1.5 signing, I updated my status comment above accordingly.
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
@rustbot label: +O-windows
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:
We are still willing to sponsor free signing key storage in Fortanix DSM.
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.
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.
5 years later, still havent managed to sign the executable. Suprised you managed to setup https
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.
@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!!
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.
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.
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. Ifrustup-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/)