Skip to content

Commit c8f43d5

Browse files
authored
Remove S.Sec.Crypto.Algorithms package dependency (#85701)
* Remove S.Sec.Crypto.Algorithms package dependency Contributes to #85641 System.Security.Cryptography.Algorithms/4.3.1 is being referenced in a few .NET Framework builds. The reference to that package is undesirable as it brings in the entire netstandard 1.x dependency graph. The only type used from that package is IncrementalHash which isn't available as a public API in .NET Framework before 4.7.1. Because of that, polyfill the netfx code in with minor changes to the source to make the analyzers happy.
1 parent bda61b9 commit c8f43d5

File tree

8 files changed

+281
-24
lines changed

8 files changed

+281
-24
lines changed

eng/Versions.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@
128128
<SystemMemoryVersion>4.5.5</SystemMemoryVersion>
129129
<SystemReflectionMetadataVersion>6.0.1</SystemReflectionMetadataVersion>
130130
<SystemSecurityAccessControlVersion>6.0.0</SystemSecurityAccessControlVersion>
131-
<SystemSecurityCryptographyAlgorithmsVersion>4.3.1</SystemSecurityCryptographyAlgorithmsVersion>
132131
<SystemSecurityCryptographyCngVersion>5.0.0</SystemSecurityCryptographyCngVersion>
133132
<SystemSecurityCryptographyOpenSslVersion>5.0.0</SystemSecurityCryptographyOpenSslVersion>
134133
<SystemSecurityPrincipalWindowsVersion>5.0.0</SystemSecurityPrincipalWindowsVersion>
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#if !NETFRAMEWORK || NET471_OR_GREATER
5+
#error "This implementation of IncrementalHash should only be used for .NET Framework where it is not available"
6+
#endif
7+
8+
using System.Diagnostics;
9+
10+
namespace System.Security.Cryptography
11+
{
12+
/// <summary>
13+
/// Provides support for computing a hash or HMAC value incrementally across several segments.
14+
/// </summary>
15+
internal sealed class IncrementalHash : IDisposable
16+
{
17+
private const int NTE_BAD_ALGID = unchecked((int)0x80090008);
18+
19+
private readonly HashAlgorithmName _algorithmName;
20+
private HashAlgorithm _hash;
21+
private bool _disposed;
22+
private bool _resetPending;
23+
24+
private IncrementalHash(HashAlgorithmName name, HashAlgorithm hash)
25+
{
26+
Debug.Assert(!string.IsNullOrEmpty(name.Name));
27+
Debug.Assert(hash != null);
28+
29+
_algorithmName = name;
30+
_hash = hash;
31+
}
32+
33+
/// <summary>
34+
/// Get the name of the algorithm being performed.
35+
/// </summary>
36+
public HashAlgorithmName AlgorithmName
37+
{
38+
get { return _algorithmName; }
39+
}
40+
41+
/// <summary>
42+
/// Append the entire contents of <paramref name="data"/> to the data already processed in the hash or HMAC.
43+
/// </summary>
44+
/// <param name="data">The data to process.</param>
45+
/// <exception cref="ArgumentNullException"><paramref name="data"/> is <c>null</c>.</exception>
46+
/// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
47+
public void AppendData(byte[] data)
48+
{
49+
if (data == null)
50+
throw new ArgumentNullException(nameof(data));
51+
52+
AppendData(data, 0, data.Length);
53+
}
54+
55+
/// <summary>
56+
/// Append <paramref name="count"/> bytes of <paramref name="data"/>, starting at <paramref name="offset"/>,
57+
/// to the data already processed in the hash or HMAC.
58+
/// </summary>
59+
/// <param name="data">The data to process.</param>
60+
/// <param name="offset">The offset into the byte array from which to begin using data.</param>
61+
/// <param name="count">The number of bytes in the array to use as data.</param>
62+
/// <exception cref="ArgumentNullException"><paramref name="data"/> is <c>null</c>.</exception>
63+
/// <exception cref="ArgumentOutOfRangeException">
64+
/// <paramref name="offset"/> is out of range. This parameter requires a non-negative number.
65+
/// </exception>
66+
/// <exception cref="ArgumentOutOfRangeException">
67+
/// <paramref name="count"/> is out of range. This parameter requires a non-negative number less than
68+
/// the <see cref="Array.Length"/> value of <paramref name="data"/>.
69+
/// </exception>
70+
/// <exception cref="ArgumentException">
71+
/// <paramref name="count"/> is greater than
72+
/// <paramref name="data"/>.<see cref="Array.Length"/> - <paramref name="offset"/>.
73+
/// </exception>
74+
/// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
75+
public void AppendData(byte[] data, int offset, int count)
76+
{
77+
if (data == null)
78+
throw new ArgumentNullException(nameof(data));
79+
if (offset < 0)
80+
throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
81+
if (count < 0 || (count > data.Length))
82+
throw new ArgumentOutOfRangeException(nameof(count));
83+
if ((data.Length - count) < offset)
84+
throw new ArgumentException(SR.Argument_InvalidOffLen);
85+
if (_disposed)
86+
throw new ObjectDisposedException(nameof(IncrementalHash));
87+
88+
Debug.Assert(_hash != null);
89+
90+
if (_resetPending)
91+
{
92+
_hash.Initialize();
93+
_resetPending = false;
94+
}
95+
96+
_hash.TransformBlock(data, offset, count, null, 0);
97+
}
98+
99+
/// <summary>
100+
/// Retrieve the hash or HMAC for the data accumulated from prior calls to
101+
/// <see cref="AppendData(byte[])"/>, and return to the state the object
102+
/// was in at construction.
103+
/// </summary>
104+
/// <returns>The computed hash or HMAC.</returns>
105+
/// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
106+
public byte[] GetHashAndReset()
107+
{
108+
if (_disposed)
109+
throw new ObjectDisposedException(nameof(IncrementalHash));
110+
111+
Debug.Assert(_hash != null);
112+
113+
if (_resetPending)
114+
{
115+
// No point in setting _resetPending to false, we're about to set it to true.
116+
_hash.Initialize();
117+
}
118+
119+
_hash.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
120+
byte[] hashValue = _hash.Hash;
121+
_resetPending = true;
122+
123+
return hashValue;
124+
}
125+
126+
/// <summary>
127+
/// Release all resources used by the current instance of the
128+
/// <see cref="IncrementalHash"/> class.
129+
/// </summary>
130+
public void Dispose()
131+
{
132+
_disposed = true;
133+
134+
if (_hash != null)
135+
{
136+
_hash.Dispose();
137+
_hash = null;
138+
}
139+
}
140+
141+
/// <summary>
142+
/// Create an <see cref="IncrementalHash"/> for the algorithm specified by <paramref name="hashAlgorithm"/>.
143+
/// </summary>
144+
/// <param name="hashAlgorithm">The name of the hash algorithm to perform.</param>
145+
/// <returns>
146+
/// An <see cref="IncrementalHash"/> instance ready to compute the hash algorithm specified
147+
/// by <paramref name="hashAlgorithm"/>.
148+
/// </returns>
149+
/// <exception cref="ArgumentException">
150+
/// <paramref name="hashAlgorithm"/>.<see cref="HashAlgorithmName.Name"/> is <c>null</c>, or
151+
/// the empty string.
152+
/// </exception>
153+
/// <exception cref="CryptographicException"><paramref name="hashAlgorithm"/> is not a known hash algorithm.</exception>
154+
public static IncrementalHash CreateHash(HashAlgorithmName hashAlgorithm)
155+
{
156+
if (string.IsNullOrEmpty(hashAlgorithm.Name))
157+
throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm));
158+
159+
return new IncrementalHash(hashAlgorithm, GetHashAlgorithm(hashAlgorithm));
160+
}
161+
162+
/// <summary>
163+
/// Create an <see cref="IncrementalHash"/> for the Hash-based Message Authentication Code (HMAC)
164+
/// algorithm utilizing the hash algorithm specified by <paramref name="hashAlgorithm"/>, and a
165+
/// key specified by <paramref name="key"/>.
166+
/// </summary>
167+
/// <param name="hashAlgorithm">The name of the hash algorithm to perform within the HMAC.</param>
168+
/// <param name="key">
169+
/// The secret key for the HMAC. The key can be any length, but a key longer than the output size
170+
/// of the hash algorithm specified by <paramref name="hashAlgorithm"/> will be hashed (using the
171+
/// algorithm specified by <paramref name="hashAlgorithm"/>) to derive a correctly-sized key. Therefore,
172+
/// the recommended size of the secret key is the output size of the hash specified by
173+
/// <paramref name="hashAlgorithm"/>.
174+
/// </param>
175+
/// <returns>
176+
/// An <see cref="IncrementalHash"/> instance ready to compute the hash algorithm specified
177+
/// by <paramref name="hashAlgorithm"/>.
178+
/// </returns>
179+
/// <exception cref="ArgumentException">
180+
/// <paramref name="hashAlgorithm"/>.<see cref="HashAlgorithmName.Name"/> is <c>null</c>, or
181+
/// the empty string.
182+
/// </exception>
183+
/// <exception cref="CryptographicException"><paramref name="hashAlgorithm"/> is not a known hash algorithm.</exception>
184+
public static IncrementalHash CreateHMAC(HashAlgorithmName hashAlgorithm, byte[] key)
185+
{
186+
if (key == null)
187+
throw new ArgumentNullException(nameof(key));
188+
if (string.IsNullOrEmpty(hashAlgorithm.Name))
189+
throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm));
190+
191+
return new IncrementalHash(hashAlgorithm, GetHMAC(hashAlgorithm, key));
192+
}
193+
194+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5351", Justification = "MD5 is used when the user asks for it.")]
195+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is used when the user asks for it.")]
196+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5354", Justification = "SHA1 is used when the user asks for it.")]
197+
private static HashAlgorithm GetHashAlgorithm(HashAlgorithmName hashAlgorithm)
198+
{
199+
if (hashAlgorithm == HashAlgorithmName.MD5)
200+
return new MD5CryptoServiceProvider();
201+
if (hashAlgorithm == HashAlgorithmName.SHA1)
202+
return new SHA1CryptoServiceProvider();
203+
if (hashAlgorithm == HashAlgorithmName.SHA256)
204+
return new SHA256CryptoServiceProvider();
205+
if (hashAlgorithm == HashAlgorithmName.SHA384)
206+
return new SHA384CryptoServiceProvider();
207+
if (hashAlgorithm == HashAlgorithmName.SHA512)
208+
return new SHA512CryptoServiceProvider();
209+
210+
throw new CryptographicException(NTE_BAD_ALGID);
211+
}
212+
213+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5351", Justification = "MD5 is used when the user asks for it.")]
214+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is used when the user asks for it.")]
215+
private static HashAlgorithm GetHMAC(HashAlgorithmName hashAlgorithm, byte[] key)
216+
{
217+
if (hashAlgorithm == HashAlgorithmName.MD5)
218+
return new HMACMD5(key);
219+
if (hashAlgorithm == HashAlgorithmName.SHA1)
220+
return new HMACSHA1(key);
221+
if (hashAlgorithm == HashAlgorithmName.SHA256)
222+
return new HMACSHA256(key);
223+
if (hashAlgorithm == HashAlgorithmName.SHA384)
224+
return new HMACSHA384(key);
225+
if (hashAlgorithm == HashAlgorithmName.SHA512)
226+
return new HMACSHA512(key);
227+
228+
throw new CryptographicException(NTE_BAD_ALGID);
229+
}
230+
}
231+
}

src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
Link="Common\src\Extensions\NonCapturingTimer\NonCapturingTimer.cs" />
1717
<Compile Include="$(CommonPath)System\ThrowHelper.cs"
1818
Link="Common\System\ThrowHelper.cs" />
19+
<Compile Include="$(CommonPath)System\Security\Cryptography\IncrementalHash.netfx.cs"
20+
Link="Common\System\Security\Cryptography\IncrementalHash.cs"
21+
Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
1922
</ItemGroup>
2023

2124
<ItemGroup>
@@ -24,8 +27,4 @@
2427
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Extensions.Primitives\src\Microsoft.Extensions.Primitives.csproj" />
2528
</ItemGroup>
2629

27-
<!-- For IncrementalHash -->
28-
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
29-
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="$(SystemSecurityCryptographyAlgorithmsVersion)" />
30-
</ItemGroup>
3130
</Project>

src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@
117117
<resheader name="writer">
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120+
<data name="Argument_InvalidOffLen" xml:space="preserve">
121+
<value>Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.</value>
122+
</data>
123+
<data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
124+
<value>Non-negative number required.</value>
125+
</data>
120126
<data name="Error_FileSystemWatcherRequiredWithoutPolling" xml:space="preserve">
121127
<value>The fileSystemWatcher parameter must be non-null when pollForChanges is false.</value>
122128
</data>
@@ -126,6 +132,9 @@
126132
<data name="CannotModifyWhenFileWatcherInitialized" xml:space="preserve">
127133
<value>Cannot modify {0} once file watcher has been initialized.</value>
128134
</data>
135+
<data name="Cryptography_HashAlgorithmNameNullOrEmpty" xml:space="preserve">
136+
<value>The hash algorithm name cannot be null or empty.</value>
137+
</data>
129138
<data name="UnexpectedFileSystemInfo" xml:space="preserve">
130139
<value>Unexpected type of FileSystemInfo</value>
131140
</data>

src/libraries/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2+
23
<PropertyGroup>
34
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
45
<ExternallyShipping>false</ExternallyShipping>
@@ -9,6 +10,7 @@
910
<EnableLibraryImportGenerator>true</EnableLibraryImportGenerator>
1011
<DefineConstants Condition="'$(TargetOS)' == 'browser'">$(DefineConstants);TARGET_BROWSER</DefineConstants>
1112
</PropertyGroup>
13+
1214
<ItemGroup>
1315
<Compile Include="$(CommonTestPath)System\Security\Cryptography\SignatureSupport.cs"
1416
Link="CommonTest\System\Security\Cryptography\SignatureSupport.cs" />
@@ -24,6 +26,7 @@
2426
Link="Common\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs" />
2527
<Compile Include="$(CommonTestPath)System\IO\TempFile.cs"
2628
Link="Common\System\IO\TempFile.cs" />
29+
2730
<Compile Include="Metadata\BlobContentIdTests.cs" />
2831
<Compile Include="Metadata\BlobTests.cs" />
2932
<Compile Include="Metadata\BlobUtilitiesTests.cs" />
@@ -90,6 +93,7 @@
9093
<Compile Include="Utilities\MemoryBlockTests.cs" />
9194
<Compile Include="Utilities\OrderByTests.cs" />
9295
</ItemGroup>
96+
9397
<ItemGroup>
9498
<None Include="Resources\Namespace\NamespaceForwardedCS.cs" />
9599
<None Include="Resources\Namespace\NamespaceTests.cs" />
@@ -135,16 +139,19 @@
135139
<None Include="Resources\Misc\Signed.cs" />
136140
<EmbeddedResource Include="Resources\Misc\Signed.exe" />
137141
</ItemGroup>
142+
138143
<ItemGroup>
139144
<!-- Some internal types are needed, so we reference the implementation assembly, rather than the reference assembly. -->
140145
<ProjectReference Include="..\src\System.Reflection.Metadata.csproj" SkipUseReferenceAssembly="true" />
141146
</ItemGroup>
147+
142148
<!-- For IncrementalHash -->
143149
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
144-
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="$(SystemSecurityCryptographyAlgorithmsVersion)" />
145150
<PackageReference Include="System.ValueTuple" Version="$(SystemValueTupleVersion)" />
146151
</ItemGroup>
152+
147153
<ItemGroup Condition="'$(TargetOS)' == 'browser'">
148154
<WasmFilesToIncludeFromPublishDir Include="$(AssemblyName).dll" />
149155
</ItemGroup>
156+
150157
</Project>

src/libraries/System.Reflection.Metadata/tests/TestUtilities/SigningUtilities.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,19 @@ public static byte[] CalculateRsaSignature(IEnumerable<Blob> content, byte[] pri
2929

3030
public static byte[] CalculateSha1(IEnumerable<Blob> content)
3131
{
32-
using (var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1))
33-
{
34-
var stream = new MemoryStream();
35-
36-
foreach (var blob in content)
37-
{
38-
var segment = blob.GetBytes();
32+
MemoryStream stream = new();
3933

40-
stream.Write(segment.Array, segment.Offset, segment.Count);
34+
foreach (Blob blob in content)
35+
{
36+
var segment = blob.GetBytes();
37+
stream.Write(segment.Array, segment.Offset, segment.Count);
38+
}
4139

42-
hash.AppendData(segment.Array, segment.Offset, segment.Count);
43-
}
40+
stream.Position = 0;
4441

45-
return hash.GetHashAndReset();
42+
using (SHA1 sha1 = SHA1.Create())
43+
{
44+
return sha1.ComputeHash(stream);
4645
}
4746
}
4847

src/libraries/System.Security.Cryptography.Cose/src/Resources/Strings.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@
123123
<data name="Argument_EncodeDestinationTooSmall" xml:space="preserve">
124124
<value>The destination is too small to hold the encoded value.</value>
125125
</data>
126+
<data name="Argument_InvalidOffLen" xml:space="preserve">
127+
<value>Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.</value>
128+
</data>
129+
<data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
130+
<value>Non-negative number required.</value>
131+
</data>
126132
<data name="ContentWasDetached" xml:space="preserve">
127133
<value>Content was not included in the message (detached message), provide a content to verify.</value>
128134
</data>
@@ -156,6 +162,9 @@
156162
<data name="CriticalHeadersMustBeArrayOfAtLeastOne" xml:space="preserve">
157163
<value>Critical Headers must be a CBOR array of at least one element.</value>
158164
</data>
165+
<data name="Cryptography_HashAlgorithmNameNullOrEmpty" xml:space="preserve">
166+
<value>The hash algorithm name cannot be null or empty.</value>
167+
</data>
159168
<data name="DecodeCoseSignatureMustBeArrayOfThree" xml:space="preserve">
160169
<value>COSE Signature must be an array of three elements.</value>
161170
</data>

0 commit comments

Comments
 (0)