From ca1d582ab29cb9793e26223584bc68bcf88c44fb Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 27 Apr 2023 10:10:32 -0500 Subject: [PATCH] Avoid finalizable internal state for non-FIPS scenarios Fixes #67995 --- .../Portable/CryptographicHashProvider.cs | 6 +- .../Core/Portable/Emit/EmitOptions.cs | 2 +- .../RoslynIncrementalHash.cs | 169 ++++++++++++++++++ ....cs => RoslynIncrementalHashExtensions.cs} | 9 +- .../Portable/PEWriter/SigningUtilities.cs | 2 +- .../Test/Core/Metadata/ILValidation.cs | 3 +- .../Workspace/Solution/Checksum_Factory.cs | 6 +- .../Core/CompilerExtensions.projitems | 1 + 8 files changed, 184 insertions(+), 14 deletions(-) create mode 100644 src/Compilers/Core/Portable/InternalUtilities/RoslynIncrementalHash.cs rename src/Compilers/Core/Portable/InternalUtilities/{IncrementalHashExtensions.cs => RoslynIncrementalHashExtensions.cs} (66%) diff --git a/src/Compilers/Core/Portable/CryptographicHashProvider.cs b/src/Compilers/Core/Portable/CryptographicHashProvider.cs index a4534494ff92b..d3fcf963921b8 100644 --- a/src/Compilers/Core/Portable/CryptographicHashProvider.cs +++ b/src/Compilers/Core/Portable/CryptographicHashProvider.cs @@ -187,7 +187,7 @@ internal static ImmutableArray ComputeSha1(byte[] bytes) internal static ImmutableArray ComputeHash(HashAlgorithmName algorithmName, IEnumerable bytes) { - using (var incrementalHash = IncrementalHash.CreateHash(algorithmName)) + using (var incrementalHash = RoslynIncrementalHash.CreateHash(algorithmName)) { incrementalHash.AppendData(bytes); return ImmutableArray.Create(incrementalHash.GetHashAndReset()); @@ -196,7 +196,7 @@ internal static ImmutableArray ComputeHash(HashAlgorithmName algorithmName internal static ImmutableArray ComputeHash(HashAlgorithmName algorithmName, IEnumerable> bytes) { - using (var incrementalHash = IncrementalHash.CreateHash(algorithmName)) + using (var incrementalHash = RoslynIncrementalHash.CreateHash(algorithmName)) { incrementalHash.AppendData(bytes); return ImmutableArray.Create(incrementalHash.GetHashAndReset()); @@ -206,7 +206,7 @@ internal static ImmutableArray ComputeHash(HashAlgorithmName algorithmName internal static ImmutableArray ComputeSourceHash(ImmutableArray bytes, SourceHashAlgorithm hashAlgorithm = SourceHashAlgorithms.Default) { var algorithmName = GetAlgorithmName(hashAlgorithm); - using (var incrementalHash = IncrementalHash.CreateHash(algorithmName)) + using (var incrementalHash = RoslynIncrementalHash.CreateHash(algorithmName)) { incrementalHash.AppendData(bytes.ToArray()); return ImmutableArray.Create(incrementalHash.GetHashAndReset()); diff --git a/src/Compilers/Core/Portable/Emit/EmitOptions.cs b/src/Compilers/Core/Portable/Emit/EmitOptions.cs index 07e92a2f3371e..83476de656a26 100644 --- a/src/Compilers/Core/Portable/Emit/EmitOptions.cs +++ b/src/Compilers/Core/Portable/Emit/EmitOptions.cs @@ -368,7 +368,7 @@ internal void ValidateOptions(DiagnosticBag diagnostics, CommonMessageProvider m { try { - IncrementalHash.CreateHash(PdbChecksumAlgorithm).Dispose(); + RoslynIncrementalHash.CreateHash(PdbChecksumAlgorithm).Dispose(); } catch { diff --git a/src/Compilers/Core/Portable/InternalUtilities/RoslynIncrementalHash.cs b/src/Compilers/Core/Portable/InternalUtilities/RoslynIncrementalHash.cs new file mode 100644 index 0000000000000..b4ca4ebc13b0a --- /dev/null +++ b/src/Compilers/Core/Portable/InternalUtilities/RoslynIncrementalHash.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.Versioning; +using System.Security.Cryptography; + +namespace Roslyn.Utilities; + +/// Provides support for computing a hash value incrementally across several segments. +internal sealed class RoslynIncrementalHash : IDisposable +{ + private const int NTE_BAD_ALGID = -2146893816; + + private readonly HashAlgorithmName _algorithmName; + private HashAlgorithm _hash; + private bool _disposed; + private bool _resetPending; + + /// Gets the name of the algorithm being performed. + /// The name of the algorithm being performed. + public HashAlgorithmName AlgorithmName => _algorithmName; + + private RoslynIncrementalHash(HashAlgorithmName name, HashAlgorithm hash) + { + _algorithmName = name; + _hash = hash; + } + + /// Appends the specified data to the data already processed in the hash or HMAC. + /// The data to process. + /// is . + /// The object has already been disposed. + public void AppendData(byte[] data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + AppendData(data, 0, data.Length); + } + + /// Appends the specified number of bytes from the specified data, starting at the specified offset, to the + /// data already processed in the hash. + /// The data to process. + /// The offset into the byte array from which to begin using data. + /// The number of bytes to use from . + /// is . + /// or is negative. + /// -or- + /// is larger than the length of . + /// The sum of and is larger than the data length. + /// The object has already been disposed. + public void AppendData(byte[] data, int offset, int count) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "ArgumentOutOfRange_NeedNonNegNum"); + } + + if (count < 0 || count > data.Length) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (data.Length - count < offset) + { + throw new ArgumentException("Argument_InvalidOffLen"); + } + + if (_disposed) + { + throw new ObjectDisposedException(typeof(RoslynIncrementalHash).Name); + } + + if (_resetPending) + { + _hash.Initialize(); + _resetPending = false; + } + + _hash.TransformBlock(data, offset, count, null, 0); + } + + /// Retrieves the hash for the data accumulated from prior calls to the + /// method, and resets the object to its initial state. + /// The computed hash or HMAC. + /// The object has already been disposed. + public byte[] GetHashAndReset() + { + if (_disposed) + { + throw new ObjectDisposedException(typeof(RoslynIncrementalHash).Name); + } + + if (_resetPending) + { + _hash.Initialize(); + } + + _hash.TransformFinalBlock(Array.Empty(), 0, 0); + byte[] hash = _hash.Hash!; + _resetPending = true; + return hash; + } + + /// Releases the resources used by the current instance of the class. + public void Dispose() + { + _disposed = true; + if (_hash != null) + { + _hash.Dispose(); + _hash = null!; + } + } + + /// Creates a for the specified algorithm. + /// The name of the hash algorithm to perform. + /// An instance ready to compute the hash algorithm specified by . + /// . is or an empty string. + /// is not a known hash algorithm. + public static RoslynIncrementalHash CreateHash(HashAlgorithmName hashAlgorithm) + { + if (string.IsNullOrEmpty(hashAlgorithm.Name)) + { + throw new ArgumentException("Cryptography_HashAlgorithmNameNullOrEmpty", nameof(hashAlgorithm)); + } + return new RoslynIncrementalHash(hashAlgorithm, GetHashAlgorithm(hashAlgorithm)); + } + + private static HashAlgorithm GetHashAlgorithm(HashAlgorithmName hashAlgorithm) + { + if (hashAlgorithm == HashAlgorithmName.MD5) + { + return MD5.Create(); + } + + if (hashAlgorithm == HashAlgorithmName.SHA1) + { + return SHA1.Create(); + } + + if (hashAlgorithm == HashAlgorithmName.SHA256) + { + return SHA256.Create(); + } + + if (hashAlgorithm == HashAlgorithmName.SHA384) + { + return SHA384.Create(); + } + + if (hashAlgorithm == HashAlgorithmName.SHA512) + { + return SHA512.Create(); + } + + throw new CryptographicException(NTE_BAD_ALGID); + } +} diff --git a/src/Compilers/Core/Portable/InternalUtilities/IncrementalHashExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/RoslynIncrementalHashExtensions.cs similarity index 66% rename from src/Compilers/Core/Portable/InternalUtilities/IncrementalHashExtensions.cs rename to src/Compilers/Core/Portable/InternalUtilities/RoslynIncrementalHashExtensions.cs index f066afd5a295a..3c13e6c4dd922 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/IncrementalHashExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/RoslynIncrementalHashExtensions.cs @@ -5,13 +5,12 @@ using System; using System.Collections.Generic; using System.Reflection.Metadata; -using System.Security.Cryptography; namespace Roslyn.Utilities { - internal static class IncrementalHashExtensions + internal static class RoslynIncrementalHashExtensions { - internal static void AppendData(this IncrementalHash hash, IEnumerable blobs) + internal static void AppendData(this RoslynIncrementalHash hash, IEnumerable blobs) { foreach (var blob in blobs) { @@ -19,7 +18,7 @@ internal static void AppendData(this IncrementalHash hash, IEnumerable blo } } - internal static void AppendData(this IncrementalHash hash, IEnumerable> blobs) + internal static void AppendData(this RoslynIncrementalHash hash, IEnumerable> blobs) { foreach (var blob in blobs) { @@ -27,7 +26,7 @@ internal static void AppendData(this IncrementalHash hash, IEnumerable segment) + internal static void AppendData(this RoslynIncrementalHash hash, ArraySegment segment) { RoslynDebug.AssertNotNull(segment.Array); hash.AppendData(segment.Array, segment.Offset, segment.Count); diff --git a/src/Compilers/Core/Portable/PEWriter/SigningUtilities.cs b/src/Compilers/Core/Portable/PEWriter/SigningUtilities.cs index 57bc552a68878..ebbc86856125e 100644 --- a/src/Compilers/Core/Portable/PEWriter/SigningUtilities.cs +++ b/src/Compilers/Core/Portable/PEWriter/SigningUtilities.cs @@ -32,7 +32,7 @@ internal static byte[] CalculateRsaSignature(IEnumerable content, RSAParam internal static byte[] CalculateSha1(IEnumerable content) { - using (var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1)) + using (var hash = RoslynIncrementalHash.CreateHash(HashAlgorithmName.SHA1)) { hash.AppendData(content); return hash.GetHashAndReset(); diff --git a/src/Compilers/Test/Core/Metadata/ILValidation.cs b/src/Compilers/Test/Core/Metadata/ILValidation.cs index 714ea9e0e9002..7f3830ef3fa2a 100644 --- a/src/Compilers/Test/Core/Metadata/ILValidation.cs +++ b/src/Compilers/Test/Core/Metadata/ILValidation.cs @@ -21,6 +21,7 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.DiaSymReader.Tools; using Microsoft.Metadata.Tools; +using Roslyn.Utilities; namespace Roslyn.Test.Utilities { @@ -145,7 +146,7 @@ private static byte[] ComputeSigningHash( buffer[authenticodeOffset + i] = 0; } - using (var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1)) + using (var hash = RoslynIncrementalHash.CreateHash(HashAlgorithmName.SHA1)) { // First hash the DOS header and PE headers hash.AppendData(buffer, 0, peHeadersSize); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index 5dcf4f0a2367e..3d97b7441a796 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -23,8 +23,8 @@ internal partial class Checksum // https://github.com/dotnet/runtime/blob/f2db6d6093c54e5eeb9db2d8dcbe15b2db92ad8c/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs#L18-L19 private const int SHA256HashSizeBytes = 256 / 8; - private static readonly ObjectPool s_incrementalHashPool = - new(() => IncrementalHash.CreateHash(HashAlgorithmName.SHA256), size: 20); + private static readonly ObjectPool s_incrementalHashPool = + new(() => RoslynIncrementalHash.CreateHash(HashAlgorithmName.SHA256), size: 20); // Dedicated pools for the byte[]s we use to create checksums from two or three existing checksums. Sized to // exactly the space needed to splat the existing checksum data into the array and then hash it. @@ -234,7 +234,7 @@ public static Checksum Create(ParseOptions value, ISerializerService serializer) return Create(stream); } - private static void AppendData(IncrementalHash hash, byte[] buffer, string value) + private static void AppendData(RoslynIncrementalHash hash, byte[] buffer, string value) { var stringBytes = MemoryMarshal.AsBytes(value.AsSpan()); Debug.Assert(stringBytes.Length == value.Length * 2); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index c5462f5a04a6f..1306563393aec 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -26,6 +26,7 @@ +