diff --git a/src/Runner.Listener/SelfUpdater.cs b/src/Runner.Listener/SelfUpdater.cs index be49b2dfe..18e8cb870 100644 --- a/src/Runner.Listener/SelfUpdater.cs +++ b/src/Runner.Listener/SelfUpdater.cs @@ -8,7 +8,9 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using System.Security.Cryptography; using GitHub.Services.WebApi; +using GitHub.Services.Common; using GitHub.Runner.Common; using GitHub.Runner.Sdk; @@ -256,6 +258,24 @@ namespace GitHub.Runner.Listener } // If we got this far, we know that we've successfully downloaded the runner package + // Validate Hash Matches if it is provided + using (FileStream stream = File.OpenRead(archiveFile)) + { + if (!String.IsNullOrEmpty(_targetPackage.HashValue)) + { + using (SHA256 sha256 = SHA256.Create()) + { + byte[] srcHashBytes = await sha256.ComputeHashAsync(stream); + var hash = PrimitiveExtensions.ConvertToHexString(srcHashBytes); + if (hash != _targetPackage.HashValue) + { + // Hash did not match, we can't recover from this, just throw + throw new Exception($"Computed runner hash {hash} did not match expected Runner Hash {_targetPackage.HashValue} for {_targetPackage.Filename}"); + } + Trace.Info($"Validated Runner Hash matches {_targetPackage.Filename} : {_targetPackage.HashValue}"); + } + } + } if (archiveFile.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) { ZipFile.ExtractToDirectory(archiveFile, latestRunnerDirectory); diff --git a/src/Sdk/Common/Common/Utility/HashAlgorithmExtensions.cs b/src/Sdk/Common/Common/Utility/HashAlgorithmExtensions.cs new file mode 100644 index 000000000..dce4e07e0 --- /dev/null +++ b/src/Sdk/Common/Common/Utility/HashAlgorithmExtensions.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace GitHub.Services.Common +{ + public static class HashAlgorithmExtensions + { + public static async Task ComputeHashAsync(this HashAlgorithm hashAlg, Stream inputStream) + { + byte[] buffer = new byte[4096]; + + while (true) + { + int read = await inputStream.ReadAsync(buffer, 0, buffer.Length); + if (read == 0) + break; + + hashAlg.TransformBlock(buffer, 0, read, null, 0); + } + + hashAlg.TransformFinalBlock(buffer, 0, 0); + return hashAlg.Hash; + } + } +} diff --git a/src/Sdk/Common/Common/Utility/PrimitiveExtensions.cs b/src/Sdk/Common/Common/Utility/PrimitiveExtensions.cs index 6c51e9cc4..f15bf502b 100644 --- a/src/Sdk/Common/Common/Utility/PrimitiveExtensions.cs +++ b/src/Sdk/Common/Common/Utility/PrimitiveExtensions.cs @@ -85,5 +85,19 @@ namespace GitHub.Services.Common var bytes = FromBase64StringNoPadding(base64String); return BitConverter.ToString(bytes).Replace("-", String.Empty); } + + /// + /// Converts byte array into a hex string + /// + public static String ConvertToHexString(byte[] bytes) + { + // Convert byte array to string + var sBuilder = new StringBuilder(); + for (int i = 0; i < bytes.Length; i++) + { + sBuilder.Append(bytes[i].ToString("x2")); + } + return sBuilder.ToString(); + } } }