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
Add a downloader + verifier for static.rust-lang.org #179
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> | ||
<?define VsVersion = "2013"?> | ||
<Fragment> | ||
<DirectoryRef Id="Dir_vsx_$(var.VsVersion)"> | ||
<Directory Id="Dir_gpg_$(var.VsVersion)" Name="gpg"/> | ||
</DirectoryRef> | ||
<ComponentGroup Id="CmpGroup_gpg_$(var.VsVersion)" Directory="Dir_gpg_$(var.VsVersion)"> | ||
<Component Id="Cmp_gpg2_exe_$(var.VsVersion)" Guid="{0E7D8C96-2B04-418B-807D-151B0CFDC5B8}"> | ||
<File Id="File_gpg2_exe_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\gpg2.exe" /> | ||
</Component> | ||
<Component Id="Cmp_gpgconf_exe_$(var.VsVersion)" Guid="{DC9B0D68-F3C0-4705-AEF8-5E4A35C12F52}"> | ||
<File Id="File_gpgconf_exe_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\gpgconf.exe" /> | ||
</Component> | ||
<Component Id="Cmp_libadns_1_dll_$(var.VsVersion)" Guid="{C9430CDF-2EA8-43B7-81E4-4250465AC83D}"> | ||
<File Id="File_libadns_1_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libadns-1.dll" /> | ||
</Component> | ||
<Component Id="Cmp_libassuan_0_dll_$(var.VsVersion)" Guid="{0EE3029A-94C7-4272-B6E4-EC10BA3B6841}"> | ||
<File Id="File_libassuan_0_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libassuan-0.dll" /> | ||
</Component> | ||
<Component Id="Cmp_libgcrypt_20_dll_$(var.VsVersion)" Guid="{16983C76-7A7C-4AEE-84AB-B45C3FECBC82}"> | ||
<File Id="File_libgcrypt_20_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libgcrypt-20.dll" /> | ||
</Component> | ||
<Component Id="Cmp_libgpg_error_0_dll_$(var.VsVersion)" Guid="{2ED73457-0617-4D5F-A77E-61280BDCB46A}"> | ||
<File Id="File_libgpg_error_0_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libgpg-error-0.dll" /> | ||
</Component> | ||
<Component Id="Cmp_libiconv_2_dll_$(var.VsVersion)" Guid="{215FBBA9-BBD5-4DAA-B380-99F6F79C9075}"> | ||
<File Id="File_libiconv_2_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libiconv-2.dll" /> | ||
</Component> | ||
<Component Id="Cmp_zlib1_dll_$(var.VsVersion)" Guid="{11231EF6-57F8-48CB-9224-5E3878D6A988}"> | ||
<File Id="File_zlib1_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\zlib1.dll" /> | ||
</Component> | ||
</ComponentGroup> | ||
</Fragment> | ||
</Wix> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> | ||
<?define VsVersion = "2015"?> | ||
<Fragment> | ||
<DirectoryRef Id="Dir_vsx_$(var.VsVersion)"> | ||
<Directory Id="Dir_gpg_$(var.VsVersion)" Name="gpg"/> | ||
</DirectoryRef> | ||
<ComponentGroup Id="CmpGroup_gpg_$(var.VsVersion)" Directory="Dir_gpg_$(var.VsVersion)"> | ||
<Component Id="Cmp_gpg2_exe_$(var.VsVersion)" Guid="{70DE99AA-2F68-44FA-8CAF-F88618ACCFE7}"> | ||
<File Id="File_gpg2_exe_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\gpg2.exe" /> | ||
</Component> | ||
<Component Id="Cmp_gpgconf_exe_$(var.VsVersion)" Guid="{98CDD029-DBC0-414C-AAED-7EC8A5493FBB}"> | ||
<File Id="File_gpgconf_exe_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\gpgconf.exe" /> | ||
</Component> | ||
<Component Id="Cmp_libadns_1_dll_$(var.VsVersion)" Guid="{ffa52ef1-1d46-4b71-92c8-f8ec75102362}"> | ||
<File Id="File_libadns_1_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libadns-1.dll" /> | ||
</Component> | ||
<Component Id="Cmp_libassuan_0_dll_$(var.VsVersion)" Guid="{aaabf11a-e79f-4e82-82f0-b52c4539a7a9}"> | ||
<File Id="File_libassuan_0_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libassuan-0.dll" /> | ||
</Component> | ||
<Component Id="Cmp_libgcrypt_20_dll_$(var.VsVersion)" Guid="{fa5e46c8-2179-481b-89c6-adef01e4a186}"> | ||
<File Id="File_libgcrypt_20_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libgcrypt-20.dll" /> | ||
</Component> | ||
<Component Id="Cmp_libgpg_error_0_dll_$(var.VsVersion)" Guid="{91ecb85a-8178-4930-aed1-8d35d9bf19d4}"> | ||
<File Id="File_libgpg_error_0_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libgpg-error-0.dll" /> | ||
</Component> | ||
<Component Id="Cmp_libiconv_2_dll_$(var.VsVersion)" Guid="{e59eb84a-278a-4c9f-8c4a-fca4b4d7a66a}"> | ||
<File Id="File_libiconv_2_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libiconv-2.dll" /> | ||
</Component> | ||
<Component Id="Cmp_zlib1_dll_$(var.VsVersion)" Guid="{77471145-bd88-4d86-a76e-d5a29789b1a1}"> | ||
<File Id="File_zlib1_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\zlib1.dll" /> | ||
</Component> | ||
</ComponentGroup> | ||
</Fragment> | ||
</Wix> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
using System; | ||
using System.IO; | ||
using System.Net; | ||
using System.Net.Security; | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Text; | ||
using System.Runtime.Serialization; | ||
|
||
namespace VisualRust | ||
{ | ||
public class Downloader | ||
{ | ||
private static String[] Sha256Fingerprints = | ||
{ | ||
"0C:0B:AC:4C:96:D9:F2:2C:8D:7A:00:9F:2F:48:3D:7B:46:FE:2C:60:0B:52:19:5B:B4:80:47:36:7C:03:E9:41", | ||
// all the known static.rust-lang.org cert fingerprints should be specified here. they expire every | ||
// 2 years. | ||
}; | ||
|
||
private static bool ValidateCertificate(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) | ||
{ | ||
if (sslPolicyErrors != SslPolicyErrors.None) | ||
{ | ||
return false; | ||
} | ||
|
||
var cert2 = new X509Certificate2(certificate); | ||
var time = System.DateTime.Now; | ||
|
||
if (time > cert2.NotAfter || time < cert2.NotBefore) | ||
{ | ||
// expiry | ||
return false; | ||
} | ||
|
||
var der_encoded = certificate.Export(X509ContentType.Cert); | ||
var hash = SHA256.Create().ComputeHash(der_encoded); | ||
var received_fingerprint = BitConverter.ToString(hash).Replace('-', ':'); | ||
|
||
foreach (String fingerprint in Sha256Fingerprints) | ||
{ | ||
if (fingerprint == received_fingerprint) { return true; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not a security person by any stretch of the imagination, so this might be dumb, but what does checking for exact cert fingerprint gives us? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It gives us the knowledge that the connection isn't being MITM'd by any other certificates that may be trusted by the root CA store - either added by malware, compromised certificate, or just plain malice (which has been documented) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's called certificate pinning, and browsers use it in the form of HSTS. |
||
} | ||
|
||
return false; | ||
} | ||
|
||
private static WebResponse RawDownload(String relative_path) | ||
{ | ||
HttpWebRequest wc = (HttpWebRequest)WebRequest.Create("https://static.rust-lang.org/dist/" + relative_path); | ||
wc.ServerCertificateValidationCallback = ValidateCertificate; | ||
return wc.GetResponse(); | ||
} | ||
|
||
/// <summary> | ||
/// Download and verify the signature of a file rust-lang.org, writing the contents into write_into. | ||
/// </summary> | ||
/// <exception cref="VerificationException"> | ||
/// Thrown when the signature of the downloaded file could not be verified with Rust's signing key. | ||
/// Note that the stream will still contain the contents of the file even if verification failed. But, | ||
/// if verification fails, the contents should not be trusted. | ||
/// </exception> | ||
/// <param name="relative_path">Appended to https://static.rust-lang.org/dist/ to determine the file to download</param> | ||
/// <param name="write_into">The contents of the downloaded file (but not the signature) will be written into this stream.</param> | ||
public static void Download(String relative_path, Stream write_into) | ||
{ | ||
var saved_pos = write_into.Position; | ||
MemoryStream sig = new MemoryStream(); | ||
RawDownload(relative_path).GetResponseStream().CopyTo(write_into); | ||
RawDownload(relative_path + ".asc").GetResponseStream().CopyTo(sig); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I've been pretty sloppy with the |
||
write_into.Position = 0; | ||
sig.Position = 0; | ||
var res = GPG.Gpg.Instance.Verify(write_into, sig); | ||
if (!res.Item1) | ||
{ | ||
File.WriteAllText("C:/Users/Elaine/Desktop/gpg_log.txt", res.Item2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You forgot something! You might consider There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, I did :) Thanks for the hint, I'll use those instead. |
||
throw new VerificationException(res.Item2); | ||
} | ||
} | ||
} | ||
|
||
[Serializable] | ||
public class VerificationException : Exception | ||
{ | ||
public VerificationException() | ||
{ | ||
} | ||
|
||
public VerificationException(string message) : base(message) | ||
{ | ||
} | ||
|
||
public VerificationException(string message, Exception innerException) : base(message, innerException) | ||
{ | ||
} | ||
|
||
protected VerificationException(SerializationInfo info, StreamingContext context) : base(info, context) | ||
{ | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure about this check? I thought this was covered by default checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not, was just being safe. Where are the default checks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find explicit documentation for it, but I see failures on common errors (self-signed: https://www.pcwebshop.co.uk/, wrong domain: https://tv.eurosport.com/, expired: https://testssl-expire.disig.sk/index.en.html). You will be getting
sslPolicyErrors != SslPolicyErrors.None
inside the callback in all those cases. This expiration check does nothing.