| File: System\Security\Cryptography\Shake256.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using System.Threading.Tasks; namespace System.Security.Cryptography { /// <summary> /// Computes the SHAKE256 hash for the input data. /// </summary> /// <remarks> /// This algorithm is specified by FIPS 202. The SHAKE algorithm family is an extendable-output function (XOF) /// which allows the output to be extended to any length. The size of the XOF indicates the security strength of /// the algorithm, not the output size. /// </remarks> public sealed partial class Shake256 : IDisposable { private readonly LiteXof _hashProvider; private bool _disposed; private ConcurrencyBlock _block; private bool _reading; /// <summary> /// Initializes a new instance of the <see cref="Shake256" /> class. /// </summary> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support SHAKE256. Callers can use the <see cref="IsSupported" /> property /// to determine if the platform supports SHAKE256. /// </exception> public Shake256() { CheckPlatformSupport(); _hashProvider = LiteHashProvider.CreateXof(HashAlgorithmId); } internal Shake256(LiteXof hashProvider) { _hashProvider = hashProvider; } /// <summary> /// Gets a value that indicates whether the algorithm is supported on the current platform. /// </summary> /// <value> /// <see langword="true" /> if the algorithm is supported; otherwise, <see langword="false" />. /// </value> public static bool IsSupported { get; } = HashProviderDispenser.HashSupported(HashAlgorithmId); /// <summary> /// Appends the specified data to the data already processed in the hash. /// </summary> /// <param name="data">The data to process.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="data" /> is <see langword="null" />. /// </exception> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> public void AppendData(byte[] data) { ArgumentNullException.ThrowIfNull(data); AppendData(new ReadOnlySpan<byte>(data)); } /// <summary> /// Appends the specified data to the data already processed in the hash. /// </summary> /// <param name="data">The data to process.</param> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> public void AppendData(ReadOnlySpan<byte> data) { CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { CheckReading(); _hashProvider.Append(data); } } /// <summary> /// Retrieves the hash for the data accumulated from prior calls to the <c>AppendData</c> methods, /// and resets the object to its initial state. /// </summary> /// <param name="outputLength">The size of the hash to produce.</param> /// <returns>The computed hash.</returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="outputLength" /> is negative. /// </exception> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> /// <seealso cref="GetCurrentHash(int)" /> public byte[] GetHashAndReset(int outputLength) { ArgumentOutOfRangeException.ThrowIfNegative(outputLength); CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { CheckReading(); byte[] output = new byte[outputLength]; _hashProvider.FinalizeAndReset(output); return output; } } /// <summary> /// Fills the buffer with the hash for the data accumulated from prior calls to the <c>AppendData</c> methods, /// and resets the object to its initial state. /// </summary> /// <param name="destination">The buffer to fill with the hash.</param> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> /// <seealso cref="GetCurrentHash(Span{byte})" /> public void GetHashAndReset(Span<byte> destination) { CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { CheckReading(); _hashProvider.FinalizeAndReset(destination); } } /// <summary> /// Retrieves the hash for the data accumulated from prior calls to the <c>AppendData</c> methods, /// without resetting the object to its initial state. /// </summary> /// <param name="outputLength">The size of the hash to produce.</param> /// <returns>The computed hash.</returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="outputLength" /> is negative. /// </exception> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> /// <seealso cref="GetHashAndReset(int)" /> public byte[] GetCurrentHash(int outputLength) { ArgumentOutOfRangeException.ThrowIfNegative(outputLength); CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { CheckReading(); byte[] output = new byte[outputLength]; _hashProvider.Current(output); return output; } } /// <summary> /// Fills the buffer with the hash for the data accumulated from prior calls to the <c>AppendData</c> methods, /// without resetting the object to its initial state. /// </summary> /// <param name="destination">The buffer to fill with the hash.</param> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> /// <seealso cref="GetHashAndReset(Span{byte})" /> public void GetCurrentHash(Span<byte> destination) { CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { CheckReading(); _hashProvider.Current(destination); } } /// <summary> /// Retrieves the hash for the data accumulated from prior calls to the <c>AppendData</c> methods without /// resetting the object to its initial state and allowing additional calls to continue retrieving the hash. /// </summary> /// <param name="outputLength">The size of the hash to produce.</param> /// <returns>The computed hash.</returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="outputLength" /> is negative. /// </exception> /// <exception cref="CryptographicException">An error has occurred during the operation.</exception> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support multiple reads of the hash. <see cref="GetHashAndReset(int)" /> can be used /// to perform a single operation. /// </exception> public byte[] Read(int outputLength) { ArgumentOutOfRangeException.ThrowIfNegative(outputLength); CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { byte[] output = new byte[outputLength]; _hashProvider.Read(output); _reading = true; return output; } } /// <summary> /// Fills the buffer with the hash for the data accumulated from prior calls to the <c>AppendData</c> methods without /// resetting the object to its initial state and allowing additional calls to continue retrieving the hash. /// </summary> /// <param name="destination">The buffer to fill with the hash.</param> /// <exception cref="CryptographicException">An error has occurred during the operation.</exception> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support multiple reads of the hash. <see cref="GetHashAndReset(Span{byte})" /> can be used /// to perform a single operation. /// </exception> public void Read(Span<byte> destination) { CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { _hashProvider.Read(destination); _reading = true; } } /// <summary> /// Resets the instance back to its initial state. /// </summary> /// <exception cref="CryptographicException">An error has occurred during the operation.</exception> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> public void Reset() { CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { _hashProvider.Reset(); _reading = false; } } /// <summary> /// Creates a new instance of <see cref="Shake256" /> with the existing appended data preserved. /// </summary> /// <returns>A clone of the current instance.</returns> /// <exception cref="CryptographicException">An error has occurred during the operation.</exception> /// <exception cref="InvalidOperationException"> /// The current instance is being read from and cannot be cloned. /// </exception> /// <exception cref="ObjectDisposedException">The object has already been disposed.</exception> public Shake256 Clone() { CheckDisposed(); using (ConcurrencyBlock.Enter(ref _block)) { CheckReading(); LiteXof clone = _hashProvider.Clone(); return new Shake256(clone); } } /// <summary> /// Release all resources used by the current instance of the <see cref="Shake256" /> class. /// </summary> public void Dispose() { if (!_disposed) { _hashProvider.Dispose(); _disposed = true; } } /// <summary> /// Computes the hash of data using the SHAKE256 algorithm. /// </summary> /// <param name="source">The data to hash.</param> /// <param name="outputLength">The size of the hash to produce.</param> /// <returns>The hash of the data.</returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="outputLength" /> is negative. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="source" /> is <see langword="null" />. /// </exception> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support SHAKE256. Callers can use the <see cref="IsSupported" /> property /// to determine if the platform supports SHAKE256. /// </exception> public static byte[] HashData(byte[] source, int outputLength) { ArgumentNullException.ThrowIfNull(source); return HashData(new ReadOnlySpan<byte>(source), outputLength); } /// <summary> /// Computes the hash of data using the SHAKE256 algorithm. /// </summary> /// <param name="source">The data to hash.</param> /// <param name="outputLength">The size of the hash to produce.</param> /// <returns>The hash of the data.</returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="outputLength" /> is negative. /// </exception> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support SHAKE256. Callers can use the <see cref="IsSupported" /> property /// to determine if the platform supports SHAKE256. /// </exception> public static byte[] HashData(ReadOnlySpan<byte> source, int outputLength) { ArgumentOutOfRangeException.ThrowIfNegative(outputLength); CheckPlatformSupport(); byte[] output = new byte[outputLength]; HashDataCore(source, output); return output; } /// <summary> /// Computes the hash of data using the SHAKE256 algorithm. /// </summary> /// <param name="source">The data to hash.</param> /// <param name="destination">The buffer to fill with the hash.</param> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support SHAKE256. Callers can use the <see cref="IsSupported" /> property /// to determine if the platform supports SHAKE256. /// </exception> public static void HashData(ReadOnlySpan<byte> source, Span<byte> destination) { CheckPlatformSupport(); HashDataCore(source, destination); } /// <summary> /// Computes the hash of a stream using the SHAKE256 algorithm. /// </summary> /// <param name="source">The stream to hash.</param> /// <param name="outputLength">The size of the hash to produce.</param> /// <returns>The hash of the data.</returns> /// <exception cref="ArgumentException"> /// <paramref name="source" /> does not support reading. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="outputLength" /> is negative. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="source" /> is <see langword="null" />. /// </exception> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support SHAKE256. Callers can use the <see cref="IsSupported" /> property /// to determine if the platform supports SHAKE256. /// </exception> public static byte[] HashData(Stream source, int outputLength) { ArgumentNullException.ThrowIfNull(source); ArgumentOutOfRangeException.ThrowIfNegative(outputLength); if (!source.CanRead) { throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source)); } CheckPlatformSupport(); return LiteHashProvider.XofStream(HashAlgorithmId, outputLength, source); } /// <summary> /// Computes the hash of a stream using the SHAKE256 algorithm. /// </summary> /// <param name="source">The stream to hash.</param> /// <param name="destination">The buffer to fill with the hash.</param> /// <exception cref="ArgumentException"> /// <paramref name="source" /> does not support reading. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="source" /> is <see langword="null" />. /// </exception> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support SHAKE256. Callers can use the <see cref="IsSupported" /> property /// to determine if the platform supports SHAKE256. /// </exception> public static void HashData(Stream source, Span<byte> destination) { ArgumentNullException.ThrowIfNull(source); if (!source.CanRead) { throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source)); } CheckPlatformSupport(); LiteHashProvider.XofStream(HashAlgorithmId, source, destination); } /// <summary> /// Asynchronously computes the hash of a stream using the SHAKE256 algorithm. /// </summary> /// <param name="source">The stream to hash.</param> /// <param name="destination">The buffer to fill with the hash.</param> /// <param name="cancellationToken"> /// The token to monitor for cancellation requests. /// The default value is <see cref="System.Threading.CancellationToken.None" />. /// </param> /// <returns>A <see cref="ValueTask"/> that represents the asynchronous operation.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="source" /> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="source" /> does not support reading. /// </exception> /// <exception cref="OperationCanceledException"> /// <paramref name="cancellationToken"/> has been canceled. /// </exception> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support SHAKE256. Callers can use the <see cref="IsSupported" /> property /// to determine if the platform supports SHAKE256. /// </exception> public static ValueTask HashDataAsync(Stream source, Memory<byte> destination, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(source); if (!source.CanRead) { throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source)); } CheckPlatformSupport(); return LiteHashProvider.XofStreamAsync(HashAlgorithmId, source, destination, cancellationToken); } /// <summary> /// Asynchronously computes the hash of a stream using the SHAKE256 algorithm. /// </summary> /// <param name="source">The stream to hash.</param> /// <param name="outputLength">The size of the hash to produce.</param> /// <param name="cancellationToken"> /// The token to monitor for cancellation requests. /// The default value is <see cref="System.Threading.CancellationToken.None" />. /// </param> /// <returns> /// A <see cref="ValueTask{TResult}" /> that completes with the computed hash. /// </returns> /// <exception cref="ArgumentException"> /// <paramref name="source" /> does not support reading. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="outputLength" /> is negative. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="source" /> is <see langword="null" />. /// </exception> /// <exception cref="OperationCanceledException"> /// <paramref name="cancellationToken"/> has been canceled. /// </exception> /// <exception cref="PlatformNotSupportedException"> /// The platform does not support SHAKE256. Callers can use the <see cref="IsSupported" /> property /// to determine if the platform supports SHAKE256. /// </exception> public static ValueTask<byte[]> HashDataAsync(Stream source, int outputLength, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(source); ArgumentOutOfRangeException.ThrowIfNegative(outputLength); if (!source.CanRead) { throw new ArgumentException(SR.Argument_StreamNotReadable, nameof(source)); } CheckPlatformSupport(); return LiteHashProvider.XofStreamAsync(HashAlgorithmId, outputLength, source, cancellationToken); } private static void HashDataCore(ReadOnlySpan<byte> source, Span<byte> destination) { HashProviderDispenser.OneShotHashProvider.HashDataXof(HashAlgorithmId, source, destination); } private static void CheckPlatformSupport() { if (!IsSupported) { throw new PlatformNotSupportedException(); } } private void CheckDisposed() => ObjectDisposedException.ThrowIf(_disposed, this); private void CheckReading() { if (_reading) { throw new InvalidOperationException(SR.InvalidOperation_AlreadyReading); } } } }