using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace PspCrypto.Security.Cryptography { public class HMACSHA224 : HMAC { /// /// The hash size produced by the HMAC SHA224 algorithm, in bits. /// public const int HashSizeInBits = 224; /// /// The hash size produced by the HMAC SHA24 algorithm, in bytes. /// public const int HashSizeInBytes = HashSizeInBits / 8; public HMACSHA224() : this(RandomNumberGenerator.GetBytes(BlockSize)) { } public HMACSHA224(byte[] key) { if (key is null) { throw new ArgumentNullException(nameof(key)); } HashName = HashAlgorithmNames.SHA224; _hMacCommon = new HMACCommon(HashAlgorithmNames.SHA224, key, BlockSize); base.Key = _hMacCommon.ActualKey!; // this not really needed as it'll initialize BlockSizeValue with same value it has which is 64. // we just want to be explicit in all HMAC extended classes BlockSizeValue = BlockSize; HashSizeValue = _hMacCommon.HashSizeInBits; Debug.Assert(HashSizeValue == HashSizeInBits); } public override byte[] Key { get { return base.Key; } set { ArgumentNullException.ThrowIfNull(value); _hMacCommon.ChangeKey(value); base.Key = _hMacCommon.ActualKey!; } } protected override void HashCore(byte[] rgb, int ib, int cb) => _hMacCommon.AppendHashData(rgb, ib, cb); protected override void HashCore(ReadOnlySpan source) => _hMacCommon.AppendHashData(source); protected override byte[] HashFinal() => _hMacCommon.FinalizeHashAndReset(); protected override bool TryHashFinal(Span destination, out int bytesWritten) => _hMacCommon.TryFinalizeHashAndReset(destination, out bytesWritten); public override void Initialize() => _hMacCommon.Reset(); /// /// Computes the HMAC of data using the SHA224 algorithm. /// /// The HMAC key. /// The data to HMAC. /// The HMAC of the data. /// /// or is . /// public static byte[] HashData(byte[] key, byte[] source) { ArgumentNullException.ThrowIfNull(key); ArgumentNullException.ThrowIfNull(source); return HashData(new ReadOnlySpan(key), new ReadOnlySpan(source)); } /// /// Computes the HMAC of data using the SHA224 algorithm. /// /// The HMAC key. /// The data to HMAC. /// The HMAC of the data. public static byte[] HashData(ReadOnlySpan key, ReadOnlySpan source) { byte[] buffer = new byte[HashSizeInBytes]; int written = HashData(key, source, buffer.AsSpan()); Debug.Assert(written == buffer.Length); return buffer; } /// /// Computes the HMAC of data using the SHA224 algorithm. /// /// The HMAC key. /// The data to HMAC. /// The buffer to receive the HMAC value. /// The total number of bytes written to . /// /// The buffer in is too small to hold the calculated hash /// size. The SHA224 algorithm always produces a 224-bit HMAC, or 28 bytes. /// public static int HashData(ReadOnlySpan key, ReadOnlySpan source, Span destination) { if (!TryHashData(key, source, destination, out int bytesWritten)) { throw new ArgumentException("Destination is too short.", nameof(destination)); } return bytesWritten; } /// /// Attempts to compute the HMAC of data using the SHA224 algorithm. /// /// The HMAC key. /// The data to HMAC. /// The buffer to receive the HMAC value. /// /// When this method returns, the total number of bytes written into . /// /// /// if is too small to hold the /// calculated hash, otherwise. /// public static bool TryHashData(ReadOnlySpan key, ReadOnlySpan source, Span destination, out int bytesWritten) { if (destination.Length < HashSizeInBytes) { bytesWritten = 0; return false; } bytesWritten = HashProviderDispenser.OneShotHashProvider.MacData(HashAlgorithmNames.SHA224, key, source, destination); Debug.Assert(bytesWritten == HashSizeInBytes); return true; } private HMACCommon _hMacCommon; private const int BlockSize = 64; } }