Skip to content

Commit 0416028

Browse files
authored
Refactor HashData on hash classes to use a single implementation
1 parent 232c74e commit 0416028

File tree

10 files changed

+255
-567
lines changed

10 files changed

+255
-567
lines changed

src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,7 @@
592592
<Compile Include="System\Security\Cryptography\HashAlgorithmName.cs" />
593593
<Compile Include="System\Security\Cryptography\HashAlgorithmNames.cs" />
594594
<Compile Include="System\Security\Cryptography\HashProvider.cs" />
595+
<Compile Include="System\Security\Cryptography\HashStatic.cs" />
595596
<Compile Include="System\Security\Cryptography\Helpers.cs" />
596597
<Compile Include="System\Security\Cryptography\HKDF.cs" />
597598
<Compile Include="System\Security\Cryptography\HKDFManagedImplementation.cs" />
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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+
using System.Diagnostics;
5+
using System.IO;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Internal.Cryptography;
9+
10+
namespace System.Security.Cryptography
11+
{
12+
internal interface IHashStatic
13+
{
14+
internal static abstract int HashSizeInBytes { get; }
15+
internal static abstract string HashAlgorithmName { get; }
16+
internal static abstract bool IsSupported { get; }
17+
}
18+
19+
// This class acts as a single implementation of the hash classes that the public APIs defer to.
20+
// The public APIs call these methods directly, so they need to behave as if they were public,
21+
// including parameter validation and async behavior.
22+
internal static class HashStatic<THash> where THash : IHashStatic
23+
{
24+
internal static byte[] HashData(byte[] source)
25+
{
26+
ArgumentNullException.ThrowIfNull(source);
27+
28+
return HashData(new ReadOnlySpan<byte>(source));
29+
}
30+
31+
internal static byte[] HashData(ReadOnlySpan<byte> source)
32+
{
33+
byte[] buffer = GC.AllocateUninitializedArray<byte>(THash.HashSizeInBytes);
34+
35+
int written = HashData(source, buffer.AsSpan());
36+
Debug.Assert(written == buffer.Length);
37+
38+
return buffer;
39+
}
40+
41+
internal static int HashData(ReadOnlySpan<byte> source, Span<byte> destination)
42+
{
43+
if (!TryHashData(source, destination, out int bytesWritten))
44+
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
45+
46+
return bytesWritten;
47+
}
48+
49+
internal static bool TryHashData(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
50+
{
51+
CheckPlatformSupport();
52+
53+
if (destination.Length < THash.HashSizeInBytes)
54+
{
55+
bytesWritten = 0;
56+
return false;
57+
}
58+
59+
bytesWritten = HashProviderDispenser.OneShotHashProvider.HashData(THash.HashAlgorithmName, source, destination);
60+
Debug.Assert(bytesWritten == THash.HashSizeInBytes);
61+
62+
return true;
63+
}
64+
65+
internal static int HashData(Stream source, Span<byte> destination)
66+
{
67+
ArgumentNullException.ThrowIfNull(source);
68+
69+
if (destination.Length < THash.HashSizeInBytes)
70+
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
71+
72+
if (!source.CanRead)
73+
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));
74+
75+
CheckPlatformSupport();
76+
return LiteHashProvider.HashStream(THash.HashAlgorithmName, source, destination);
77+
}
78+
79+
public static byte[] HashData(Stream source)
80+
{
81+
ArgumentNullException.ThrowIfNull(source);
82+
83+
if (!source.CanRead)
84+
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));
85+
86+
CheckPlatformSupport();
87+
return LiteHashProvider.HashStream(THash.HashAlgorithmName, THash.HashSizeInBytes, source);
88+
}
89+
90+
internal static ValueTask<byte[]> HashDataAsync(Stream source, CancellationToken cancellationToken)
91+
{
92+
ArgumentNullException.ThrowIfNull(source);
93+
94+
if (!source.CanRead)
95+
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));
96+
97+
CheckPlatformSupport();
98+
return LiteHashProvider.HashStreamAsync(THash.HashAlgorithmName, source, cancellationToken);
99+
}
100+
101+
internal static ValueTask<int> HashDataAsync(
102+
Stream source,
103+
Memory<byte> destination,
104+
CancellationToken cancellationToken)
105+
{
106+
ArgumentNullException.ThrowIfNull(source);
107+
108+
if (destination.Length < THash.HashSizeInBytes)
109+
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
110+
111+
if (!source.CanRead)
112+
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));
113+
114+
CheckPlatformSupport();
115+
return LiteHashProvider.HashStreamAsync(
116+
THash.HashAlgorithmName,
117+
source,
118+
destination,
119+
cancellationToken);
120+
}
121+
122+
internal static void CheckPlatformSupport()
123+
{
124+
if (!THash.IsSupported)
125+
throw new PlatformNotSupportedException();
126+
}
127+
}
128+
}

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MD5.cs

Lines changed: 18 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ namespace System.Security.Cryptography
1919

2020
public abstract class MD5 : HashAlgorithm
2121
{
22+
private sealed class HashTrait : IHashStatic
23+
{
24+
static int IHashStatic.HashSizeInBytes => HashSizeInBytes;
25+
static string IHashStatic.HashAlgorithmName => HashAlgorithmNames.MD5;
26+
27+
// Even though MD5 is not supported on browser, we return true and let it act as an unknown algorithm
28+
// instead of an unsupported algorithm.
29+
static bool IHashStatic.IsSupported => true;
30+
}
31+
2232
/// <summary>
2333
/// The hash size produced by the MD5 algorithm, in bits.
2434
/// </summary>
@@ -50,28 +60,15 @@ protected MD5()
5060
/// <paramref name="source" /> is <see langword="null" />.
5161
/// </exception>
5262
[UnsupportedOSPlatform("browser")]
53-
public static byte[] HashData(byte[] source)
54-
{
55-
ArgumentNullException.ThrowIfNull(source);
56-
57-
return HashData(new ReadOnlySpan<byte>(source));
58-
}
63+
public static byte[] HashData(byte[] source) => HashStatic<HashTrait>.HashData(source);
5964

6065
/// <summary>
6166
/// Computes the hash of data using the MD5 algorithm.
6267
/// </summary>
6368
/// <param name="source">The data to hash.</param>
6469
/// <returns>The hash of the data.</returns>
6570
[UnsupportedOSPlatform("browser")]
66-
public static byte[] HashData(ReadOnlySpan<byte> source)
67-
{
68-
byte[] buffer = GC.AllocateUninitializedArray<byte>(HashSizeInBytes);
69-
70-
int written = HashData(source, buffer.AsSpan());
71-
Debug.Assert(written == buffer.Length);
72-
73-
return buffer;
74-
}
71+
public static byte[] HashData(ReadOnlySpan<byte> source) => HashStatic<HashTrait>.HashData(source);
7572

7673
/// <summary>
7774
/// Computes the hash of data using the MD5 algorithm.
@@ -86,10 +83,7 @@ public static byte[] HashData(ReadOnlySpan<byte> source)
8683
[UnsupportedOSPlatform("browser")]
8784
public static int HashData(ReadOnlySpan<byte> source, Span<byte> destination)
8885
{
89-
if (!TryHashData(source, destination, out int bytesWritten))
90-
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
91-
92-
return bytesWritten;
86+
return HashStatic<HashTrait>.HashData(source, destination);
9387
}
9488

9589
/// <summary>
@@ -107,16 +101,7 @@ public static int HashData(ReadOnlySpan<byte> source, Span<byte> destination)
107101
[UnsupportedOSPlatform("browser")]
108102
public static bool TryHashData(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
109103
{
110-
if (destination.Length < HashSizeInBytes)
111-
{
112-
bytesWritten = 0;
113-
return false;
114-
}
115-
116-
bytesWritten = HashProviderDispenser.OneShotHashProvider.HashData(HashAlgorithmNames.MD5, source, destination);
117-
Debug.Assert(bytesWritten == HashSizeInBytes);
118-
119-
return true;
104+
return HashStatic<HashTrait>.TryHashData(source, destination, out bytesWritten);
120105
}
121106

122107
/// <summary>
@@ -141,15 +126,7 @@ public static bool TryHashData(ReadOnlySpan<byte> source, Span<byte> destination
141126
[UnsupportedOSPlatform("browser")]
142127
public static int HashData(Stream source, Span<byte> destination)
143128
{
144-
ArgumentNullException.ThrowIfNull(source);
145-
146-
if (destination.Length < HashSizeInBytes)
147-
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
148-
149-
if (!source.CanRead)
150-
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));
151-
152-
return LiteHashProvider.HashStream(HashAlgorithmNames.MD5, source, destination);
129+
return HashStatic<HashTrait>.HashData(source, destination);
153130
}
154131

155132
/// <summary>
@@ -164,15 +141,7 @@ public static int HashData(Stream source, Span<byte> destination)
164141
/// <paramref name="source" /> does not support reading.
165142
/// </exception>
166143
[UnsupportedOSPlatform("browser")]
167-
public static byte[] HashData(Stream source)
168-
{
169-
ArgumentNullException.ThrowIfNull(source);
170-
171-
if (!source.CanRead)
172-
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));
173-
174-
return LiteHashProvider.HashStream(HashAlgorithmNames.MD5, HashSizeInBytes, source);
175-
}
144+
public static byte[] HashData(Stream source) => HashStatic<HashTrait>.HashData(source);
176145

177146
/// <summary>
178147
/// Asynchronously computes the hash of a stream using the MD5 algorithm.
@@ -192,12 +161,7 @@ public static byte[] HashData(Stream source)
192161
[UnsupportedOSPlatform("browser")]
193162
public static ValueTask<byte[]> HashDataAsync(Stream source, CancellationToken cancellationToken = default)
194163
{
195-
ArgumentNullException.ThrowIfNull(source);
196-
197-
if (!source.CanRead)
198-
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));
199-
200-
return LiteHashProvider.HashStreamAsync(HashAlgorithmNames.MD5, source, cancellationToken);
164+
return HashStatic<HashTrait>.HashDataAsync(source, cancellationToken);
201165
}
202166

203167
/// <summary>
@@ -229,19 +193,7 @@ public static ValueTask<int> HashDataAsync(
229193
Memory<byte> destination,
230194
CancellationToken cancellationToken = default)
231195
{
232-
ArgumentNullException.ThrowIfNull(source);
233-
234-
if (destination.Length < HashSizeInBytes)
235-
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
236-
237-
if (!source.CanRead)
238-
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));
239-
240-
return LiteHashProvider.HashStreamAsync(
241-
HashAlgorithmNames.MD5,
242-
source,
243-
destination,
244-
cancellationToken);
196+
return HashStatic<HashTrait>.HashDataAsync(source, destination, cancellationToken);
245197
}
246198

247199
private sealed class Implementation : MD5

0 commit comments

Comments
 (0)