Skip to content

Implement hash and HMAC stream one shots #63757

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 23, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ internal static partial class AppleCrypto
internal static partial SafeHmacHandle HmacCreate(PAL_HashAlgorithm algorithm, ref int cbDigest);

[GeneratedDllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_HmacInit")]
internal static partial int HmacInit(SafeHmacHandle ctx, byte[] pbKey, int cbKey);
private static unsafe partial int HmacInit(SafeHmacHandle ctx, byte* pbKey, int cbKey);

internal static unsafe int HmacInit(SafeHmacHandle ctx, ReadOnlySpan<byte> key)
{
fixed (byte* pKey = &MemoryMarshal.GetReference(key))
{
return HmacInit(ctx, pKey, key.Length);
}
}

internal static int HmacUpdate(SafeHmacHandle ctx, ReadOnlySpan<byte> data) =>
HmacUpdate(ctx, ref MemoryMarshal.GetReference(data), data.Length);
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@
<Compile Include="System\Security\Cryptography\IncrementalHash.cs" />
<Compile Include="System\Security\Cryptography\KeyedHashAlgorithm.cs" />
<Compile Include="System\Security\Cryptography\KeySizes.cs" />
<Compile Include="System\Security\Cryptography\LiteHashProvider.cs" />
<Compile Include="System\Security\Cryptography\MaskGenerationMethod.cs" />
<Compile Include="System\Security\Cryptography\MD5.cs" />
<Compile Include="System\Security\Cryptography\Oid.cs" />
Expand Down Expand Up @@ -378,6 +379,7 @@
<Compile Include="System\Security\Cryptography\RSA.Create.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\SHAHashProvider.Browser.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Browser.cs" />
</ItemGroup>
<ItemGroup Condition="'$(NeedOpenSslInitializer)' == 'true'">
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
Expand Down Expand Up @@ -482,6 +484,7 @@
<Compile Include="System\Security\Cryptography\RandomNumberGeneratorImplementation.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\RC2Implementation.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Unix.cs" />
</ItemGroup>
<ItemGroup Condition="'$(UseOpenSslAead)' == 'true'">
<Compile Include="$(CommonPath)Interop\Unix\System.Security.Cryptography.Native\Interop.OpenSslAvailable.cs"
Expand Down Expand Up @@ -571,6 +574,7 @@
<Compile Include="System\Security\Cryptography\RC2Implementation.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\RSA.Create.Android.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Unix.cs" />
</ItemGroup>
<ItemGroup Condition="'$(UseAppleCrypto)' == 'true'">
<Compile Include="$(CommonPath)Internal\Cryptography\AsymmetricAlgorithmHelpers.Ansi.cs"
Expand Down Expand Up @@ -637,9 +641,11 @@
<Compile Include="System\Security\Cryptography\DesImplementation.Apple.cs" />
<Compile Include="System\Security\Cryptography\ECDiffieHellman.Create.SecurityTransforms.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.Apple.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Apple.cs" />
<Compile Include="System\Security\Cryptography\RandomNumberGeneratorImplementation.Apple.cs" />
<Compile Include="System\Security\Cryptography\RC2Implementation.Apple.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.Apple.cs" />
<Compile Include="System\Security\Cryptography\HashAlgorithmNames.Apple.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsOSX)' == 'true'">
<Compile Include="$(CommonPath)Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Pbkdf2.cs"
Expand Down Expand Up @@ -840,6 +846,7 @@
<Compile Include="System\Security\Cryptography\RC2Implementation.Windows.cs" />
<Compile Include="System\Security\Cryptography\RSACng.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.Windows.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Windows.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Collections" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.IO;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using Internal.Cryptography;

namespace System.Security.Cryptography
Expand Down Expand Up @@ -162,6 +165,175 @@ public static bool TryHashData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> source
return true;
}

/// <summary>
/// Computes the HMAC of a stream using the MD5 algorithm.
/// </summary>
/// <param name="key">The HMAC key.</param>
/// <param name="source">The stream to HMAC.</param>
/// <param name="destination">The buffer to receive the HMAC value.</param>
/// <returns>The total number of bytes written to <paramref name="destination" />.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <p>
/// The buffer in <paramref name="destination"/> is too small to hold the calculated HMAC
/// size. The MD5 algorithm always produces a 128-bit HMAC, or 16 bytes.
/// </p>
/// <p>-or-</p>
/// <p>
/// <paramref name="source" /> does not support reading.
/// </p>
/// </exception>
public static int HashData(ReadOnlySpan<byte> key, Stream source, Span<byte> destination)
{
ArgumentNullException.ThrowIfNull(source);

if (destination.Length < HashSizeInBytes)
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));

if (!source.CanRead)
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));

return LiteHashProvider.HmacStream(HashAlgorithmNames.MD5, HashSizeInBytes, key, source, destination);
}

/// <summary>
/// Computes the HMAC of a stream using the MD5 algorithm.
/// </summary>
/// <param name="key">The HMAC key.</param>
/// <param name="source">The stream to HMAC.</param>
/// <returns>The HMAC of the data.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="source" /> does not support reading.
/// </exception>
public static byte[] HashData(ReadOnlySpan<byte> key, Stream source)
{
ArgumentNullException.ThrowIfNull(source);

if (!source.CanRead)
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));

return LiteHashProvider.HmacStream(HashAlgorithmNames.MD5, HashSizeInBytes, key, source);
}

/// <summary>
/// Computes the HMAC of a stream using the MD5 algorithm.
/// </summary>
/// <param name="key">The HMAC key.</param>
/// <param name="source">The stream to HMAC.</param>
/// <returns>The HMAC of the data.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="key" /> or <paramref name="source" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="source" /> does not support reading.
/// </exception>
public static byte[] HashData(byte[] key, Stream source)
{
ArgumentNullException.ThrowIfNull(key);
return HashData(new ReadOnlySpan<byte>(key), source);
}

/// <summary>
/// Asynchronously computes the HMAC of a stream using the MD5 algorithm.
/// </summary>
/// <param name="key">The HMAC key.</param>
/// <param name="source">The stream to HMAC.</param>
/// <param name="cancellationToken">
/// The token to monitor for cancellation requests.
/// The default value is <see cref="System.Threading.CancellationToken.None" />.
/// </param>
/// <returns>The HMAC of the data.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="source" /> does not support reading.
/// </exception>
public static ValueTask<byte[]> HashDataAsync(ReadOnlyMemory<byte> key, Stream source, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(source);

if (!source.CanRead)
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));

return LiteHashProvider.HmacStreamAsync(HashAlgorithmNames.MD5, HashSizeInBytes, key.Span, source, cancellationToken);
}

/// <summary>
/// Asynchronously computes the HMAC of a stream using the MD5 algorithm.
/// </summary>
/// <param name="key">The HMAC key.</param>
/// <param name="source">The stream to HMAC.</param>
/// <param name="cancellationToken">
/// The token to monitor for cancellation requests.
/// The default value is <see cref="System.Threading.CancellationToken.None" />.
/// </param>
/// <returns>The HMAC of the data.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="key" /> or <paramref name="source" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="source" /> does not support reading.
/// </exception>
public static ValueTask<byte[]> HashDataAsync(byte[] key, Stream source, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(key);

return HashDataAsync(new ReadOnlyMemory<byte>(key), source, cancellationToken);
}

/// <summary>
/// Asynchronously computes the HMAC of a stream using the MD5 algorithm.
/// </summary>
/// <param name="key">The HMAC key.</param>
/// <param name="source">The stream to HMAC.</param>
/// <param name="destination">The buffer to receive the HMAC value.</param>
/// <param name="cancellationToken">
/// The token to monitor for cancellation requests.
/// The default value is <see cref="System.Threading.CancellationToken.None" />.
/// </param>
/// <returns>The total number of bytes written to <paramref name="destination" />.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <p>
/// The buffer in <paramref name="destination"/> is too small to hold the calculated hash
/// size. The MD5 algorithm always produces a 128-bit hash, or 16 bytes.
/// </p>
/// <p>-or-</p>
/// <p>
/// <paramref name="source" /> does not support reading.
/// </p>
/// </exception>
public static ValueTask<int> HashDataAsync(
ReadOnlyMemory<byte> key,
Stream source,
Memory<byte> destination,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(source);

if (destination.Length < HashSizeInBytes)
throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));

if (!source.CanRead)
throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source));

return LiteHashProvider.HmacStreamAsync(
HashAlgorithmNames.MD5,
HashSizeInBytes,
key.Span,
source,
destination,
cancellationToken);
}

protected override void Dispose(bool disposing)
{
if (disposing)
Expand Down
Loading