diff --git a/VisualRust.Setup/VisualRust.Setup.wixproj b/VisualRust.Setup/VisualRust.Setup.wixproj
index eaa9fb33..bcf66874 100644
--- a/VisualRust.Setup/VisualRust.Setup.wixproj
+++ b/VisualRust.Setup/VisualRust.Setup.wixproj
@@ -38,6 +38,8 @@
obj\$(Platform)\$(Configuration)\
+
+
diff --git a/VisualRust.Setup/gpg2013.wxs b/VisualRust.Setup/gpg2013.wxs
new file mode 100644
index 00000000..74e25581
--- /dev/null
+++ b/VisualRust.Setup/gpg2013.wxs
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VisualRust.Setup/gpg2015.wxs b/VisualRust.Setup/gpg2015.wxs
new file mode 100644
index 00000000..7fe85323
--- /dev/null
+++ b/VisualRust.Setup/gpg2015.wxs
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VisualRust.Setup/vsx2013.wxs b/VisualRust.Setup/vsx2013.wxs
index cf2929b5..7e60c97e 100644
--- a/VisualRust.Setup/vsx2013.wxs
+++ b/VisualRust.Setup/vsx2013.wxs
@@ -37,6 +37,8 @@
+
+
diff --git a/VisualRust.Setup/vsx2015.wxs b/VisualRust.Setup/vsx2015.wxs
index 37298d99..fed91ea0 100644
--- a/VisualRust.Setup/vsx2015.wxs
+++ b/VisualRust.Setup/vsx2015.wxs
@@ -37,6 +37,8 @@
+
+
diff --git a/VisualRust/Downloader.cs b/VisualRust/Downloader.cs
new file mode 100644
index 00000000..943a48b2
--- /dev/null
+++ b/VisualRust/Downloader.cs
@@ -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; }
+ }
+
+ 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();
+ }
+
+ ///
+ /// Download and verify the signature of a file rust-lang.org, writing the contents into write_into.
+ ///
+ ///
+ /// 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.
+ ///
+ /// Appended to https://static.rust-lang.org/dist/ to determine the file to download
+ /// The contents of the downloaded file (but not the signature) will be written into this stream.
+ 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);
+ 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);
+ 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)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/VisualRust/Forms/RustOptionsPage.cs b/VisualRust/Forms/RustOptionsPage.cs
index 7125984d..f849041c 100644
--- a/VisualRust/Forms/RustOptionsPage.cs
+++ b/VisualRust/Forms/RustOptionsPage.cs
@@ -15,6 +15,12 @@ public class RustOptionsPage : DialogPage
public bool UseCustomSources { get; set; }
public string CustomSourcesPath { get; set; }
+ public bool UseCustomGpg { get; set; }
+ public string CustomGpgPath { get; set; }
+
+ public bool UseCustomGpgHomedir { get; set; }
+ public string CustomGpgHomedir { get; set; }
+
private RustOptionsPageControl _page;
protected override IWin32Window Window
diff --git a/VisualRust/Racer/RacerSingleton.cs b/VisualRust/Racer/RacerSingleton.cs
index 1ab991d9..9eaa2c8a 100644
--- a/VisualRust/Racer/RacerSingleton.cs
+++ b/VisualRust/Racer/RacerSingleton.cs
@@ -51,23 +51,18 @@ public static void Init()
private RacerSingleton()
{
}
-
- private T GetVisualRustProperty(DTE env, string key)
- {
- return (T)env.get_Properties("Visual Rust", "General").Item(key).Value;
- }
-
+
private void ReinitializeRacerPaths()
{
DTE env = (DTE)VisualRustPackage.GetGlobalService(typeof(DTE));
// If path to racer.exe is specifed, use it
- if(GetVisualRustProperty(env, "UseCustomRacer"))
- racerPathForExecution = GetVisualRustProperty(env, "CustomRacerPath");
+ if(Utils.GetVisualRustProperty(env, "UseCustomRacer"))
+ racerPathForExecution = Utils.GetVisualRustProperty(env, "CustomRacerPath");
else
racerPathForExecution = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Racer", BundledRacerExecutable);
// Same for custom RUST_SRC_PATH
- if(GetVisualRustProperty(env, "UseCustomSources"))
- racerSourcesLocation = GetVisualRustProperty(env, "CustomSourcesPath");
+ if(Utils.GetVisualRustProperty(env, "UseCustomSources"))
+ racerSourcesLocation = Utils.GetVisualRustProperty(env, "CustomSourcesPath");
else
racerSourcesLocation = null;
}
@@ -136,27 +131,5 @@ private string Exec(string args)
return "";
}
}
-
- class WindowsErrorMode : IDisposable
- {
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern int SetErrorMode(int wMode);
-
- private readonly int oldErrorMode;
-
- ///
- /// Creates a new error mode context.
- ///
- /// Error mode to use. 3 is a useful value.
- public WindowsErrorMode(int mode)
- {
- oldErrorMode = SetErrorMode(mode);
- }
-
- public void Dispose()
- {
- SetErrorMode(oldErrorMode);
- }
- }
}
}
diff --git a/VisualRust/Utils.cs b/VisualRust/Utils.cs
index 4faea8c0..1b9dc9ed 100644
--- a/VisualRust/Utils.cs
+++ b/VisualRust/Utils.cs
@@ -17,6 +17,8 @@ namespace VisualRust
using RustLexer;
using Microsoft.VisualStudio.Text;
using Antlr4.Runtime;
+ using System.Runtime.InteropServices;
+ using EnvDTE;
static class Utils
{
@@ -293,6 +295,11 @@ internal static Tuple GetTokensAtPosition(SnapshotPoint snapshot
return Tuple.Create(leftToken, currentToken);
}
+
+ public static T GetVisualRustProperty(DTE env, string key)
+ {
+ return (T)env.get_Properties("Visual Rust", "General").Item(key).Value;
+ }
}
public class TemporaryFile : IDisposable
@@ -327,4 +334,25 @@ public void Dispose()
}
}
+ public class WindowsErrorMode : IDisposable
+ {
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern int SetErrorMode(int wMode);
+
+ private readonly int oldErrorMode;
+
+ ///
+ /// Creates a new error mode context.
+ ///
+ /// Error mode to use. 3 is a useful value.
+ public WindowsErrorMode(int mode)
+ {
+ oldErrorMode = SetErrorMode(mode);
+ }
+
+ public void Dispose()
+ {
+ SetErrorMode(oldErrorMode);
+ }
+ }
}
diff --git a/VisualRust/VisualRust.csproj b/VisualRust/VisualRust.csproj
index cb132a2c..029cf592 100644
--- a/VisualRust/VisualRust.csproj
+++ b/VisualRust/VisualRust.csproj
@@ -212,6 +212,8 @@
+
+
@@ -289,6 +291,38 @@
LICENSE.txt
true
+
+ true
+ PreserveNewest
+
+
+ true
+ PreserveNewest
+
+
+ true
+ PreserveNewest
+
+
+ true
+ PreserveNewest
+
+
+ true
+ PreserveNewest
+
+
+ true
+ PreserveNewest
+
+
+ true
+ PreserveNewest
+
+
+ true
+ PreserveNewest
+
PreserveNewest
true
diff --git a/VisualRust/VisualRustPackage.cs b/VisualRust/VisualRustPackage.cs
index dd82db23..4e3788f1 100644
--- a/VisualRust/VisualRustPackage.cs
+++ b/VisualRust/VisualRustPackage.cs
@@ -101,6 +101,7 @@ protected override void Initialize()
docEventsListener = new RunningDocTableEventsListener((IVsRunningDocumentTable)GetService(typeof(SVsRunningDocumentTable)));
Racer.RacerSingleton.Init();
+ GPG.Gpg.Init();
}
protected override void Dispose(bool disposing)
diff --git a/VisualRust/gpg/Gpg.cs b/VisualRust/gpg/Gpg.cs
new file mode 100644
index 00000000..751c7841
--- /dev/null
+++ b/VisualRust/gpg/Gpg.cs
@@ -0,0 +1,121 @@
+using EnvDTE;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Process = System.Diagnostics.Process;
+
+namespace VisualRust.GPG
+{
+ ///
+ /// Wrapper for the native gpg2.exe binary
+ ///
+ public class Gpg
+ {
+ private string homedir;
+ private string gpgPath;
+ private const string BundledGpgExecutable = "gpg2.exe";
+ private const int TimeoutMillis = 3000;
+ private static Gpg instance;
+
+ ///
+ /// Gets the singleton instance.
+ ///
+ public static Gpg Instance
+ {
+ get
+ {
+ if (instance == null)
+ Init();
+ return instance;
+ }
+ }
+
+ ///
+ /// Initializes the environment for the racer autocompleter.
+ /// Can be called from package/command init to init ahead of first use.
+ ///
+ public static void Init()
+ {
+ if (instance == null)
+ instance = new Gpg();
+ }
+
+ private void ReinitializeGpgPaths()
+ {
+ DTE env = (DTE)VisualRustPackage.GetGlobalService(typeof(DTE));
+ if (Utils.GetVisualRustProperty(env, "UseCustomGpg"))
+ gpgPath = Utils.GetVisualRustProperty(env, "CustomGpgPath");
+ else
+ gpgPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "gpg", BundledGpgExecutable);
+
+ if (Utils.GetVisualRustProperty(env, "UseCustomGpgHomedir"))
+ homedir = Utils.GetVisualRustProperty(env, "CustomGpgHomedir");
+ else
+ homedir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "gpg");
+ }
+
+ private Gpg()
+ {
+ ReinitializeGpgPaths();
+ }
+
+ private Tuple Exec(string args, Stream stdin)
+ {
+ try
+ {
+ using (new WindowsErrorMode(3))
+ using (Process process = new Process())
+ {
+
+ process.StartInfo.FileName = gpgPath;
+ process.StartInfo.Arguments = args;
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardInput = true;
+ process.StartInfo.RedirectStandardError = true;
+ process.StartInfo.CreateNoWindow = true;
+
+ process.Start();
+
+ stdin.CopyTo(process.StandardInput.BaseStream);
+ process.StandardInput.Close();
+ string result = process.StandardOutput.ReadToEnd();
+ result += process.StandardError.ReadToEnd();
+ process.WaitForExit();
+
+ return Tuple.Create(process.ExitCode, result);
+ }
+ }
+ catch (Exception ex)
+ {
+ Utils.DebugPrintToOutput("Error executing gpg2.exe: {0}", ex);
+ return null;
+ }
+ }
+
+ ///
+ /// Verify that data is validly signed.
+ ///
+ /// Stream to verify signature of.
+ /// true if the stream is signed with a key in the keyring, false otherwise.
+ public Tuple Verify(Stream data_to_verify, Stream signature)
+ {
+ // We could avoid hitting the filesystem, and ideally we'd be using GPGME instead of shelling out, but this is... far easier.
+ using (var tmp = new TemporaryFile(""))
+ {
+ using (var f = new FileStream(tmp.Path, FileMode.OpenOrCreate))
+ {
+ signature.CopyTo(f);
+ f.Flush();
+ }
+ var res = Exec("--verify --no-permission-warning --keyring \"" + homedir + "\\rust-key.gpg\" " + tmp.Path + " -",
+ data_to_verify);
+ return Tuple.Create(res.Item1 == 0, res.Item2);
+ }
+ }
+ }
+}
diff --git a/VisualRust/gpg/gpg2.exe b/VisualRust/gpg/gpg2.exe
new file mode 100644
index 00000000..d6661e06
Binary files /dev/null and b/VisualRust/gpg/gpg2.exe differ
diff --git a/VisualRust/gpg/gpgconf.exe b/VisualRust/gpg/gpgconf.exe
new file mode 100644
index 00000000..0b91238a
Binary files /dev/null and b/VisualRust/gpg/gpgconf.exe differ
diff --git a/VisualRust/gpg/libadns-1.dll b/VisualRust/gpg/libadns-1.dll
new file mode 100644
index 00000000..67cf70ec
Binary files /dev/null and b/VisualRust/gpg/libadns-1.dll differ
diff --git a/VisualRust/gpg/libassuan-0.dll b/VisualRust/gpg/libassuan-0.dll
new file mode 100644
index 00000000..0adbcfd0
Binary files /dev/null and b/VisualRust/gpg/libassuan-0.dll differ
diff --git a/VisualRust/gpg/libgcrypt-20.dll b/VisualRust/gpg/libgcrypt-20.dll
new file mode 100644
index 00000000..82d61a58
Binary files /dev/null and b/VisualRust/gpg/libgcrypt-20.dll differ
diff --git a/VisualRust/gpg/libgpg-error-0.dll b/VisualRust/gpg/libgpg-error-0.dll
new file mode 100644
index 00000000..6db71018
Binary files /dev/null and b/VisualRust/gpg/libgpg-error-0.dll differ
diff --git a/VisualRust/gpg/libiconv-2.dll b/VisualRust/gpg/libiconv-2.dll
new file mode 100644
index 00000000..52ddcd1a
Binary files /dev/null and b/VisualRust/gpg/libiconv-2.dll differ
diff --git a/VisualRust/gpg/rust-key.gpg b/VisualRust/gpg/rust-key.gpg
new file mode 100644
index 00000000..bc448d26
Binary files /dev/null and b/VisualRust/gpg/rust-key.gpg differ
diff --git a/VisualRust/gpg/zlib1.dll b/VisualRust/gpg/zlib1.dll
new file mode 100644
index 00000000..01926599
Binary files /dev/null and b/VisualRust/gpg/zlib1.dll differ