File: System\Security\Cryptography\LiteHash.Unix.cs
Web Access
Project: src\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.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
 
namespace System.Security.Cryptography
{
    internal static partial class LiteHashProvider
    {
        internal static LiteHash CreateHash(string hashAlgorithmId)
        {
            IntPtr algorithm = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId);
            return new LiteHash(algorithm);
        }
 
        internal static LiteHmac CreateHmac(string hashAlgorithmId, ReadOnlySpan<byte> key)
        {
            IntPtr algorithm = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId);
            return new LiteHmac(algorithm, key);
        }
 
        internal static LiteXof CreateXof(string hashAlgorithmId)
        {
            IntPtr algorithm = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId);
            return new LiteXof(algorithm);
        }
    }
 
    internal readonly struct LiteXof : ILiteHash
    {
        private readonly SafeEvpMdCtxHandle _ctx;
        private readonly IntPtr _algorithm;
 
        public int HashSizeInBytes => throw new NotSupportedException();
 
        internal LiteXof(IntPtr algorithm)
        {
            Debug.Assert(algorithm != IntPtr.Zero);
            _algorithm = algorithm;
 
            _ctx = Interop.Crypto.EvpMdCtxCreate(algorithm);
            Interop.Crypto.CheckValidOpenSslHandle(_ctx);
        }
 
        private LiteXof(SafeEvpMdCtxHandle ctx, IntPtr algorithm)
        {
            _ctx = ctx;
            _algorithm = algorithm;
        }
 
        public void Append(ReadOnlySpan<byte> data)
        {
            if (data.IsEmpty)
            {
                return;
            }
 
            Check(Interop.Crypto.EvpDigestUpdate(_ctx, data, data.Length));
        }
 
        public void Reset()
        {
            Check(Interop.Crypto.EvpDigestReset(_ctx, _algorithm));
        }
 
        public int Finalize(Span<byte> destination)
        {
            Check(Interop.Crypto.EvpDigestFinalXOF(_ctx, destination));
            return destination.Length;
        }
 
        public int FinalizeAndReset(Span<byte> destination)
        {
            int written = Finalize(destination);
            Reset();
            return written;
        }
 
        public void Current(Span<byte> destination)
        {
            Check(Interop.Crypto.EvpDigestCurrentXOF(_ctx, destination));
        }
 
        public LiteXof Clone()
        {
            SafeEvpMdCtxHandle clone = Interop.Crypto.EvpMdCtxCopyEx(_ctx);
            Interop.Crypto.CheckValidOpenSslHandle(clone);
            return new LiteXof(clone, _algorithm);
        }
 
        public void Read(Span<byte> destination)
        {
            Check(Interop.Crypto.EvpDigestSqueeze(_ctx, destination));
        }
 
        public void Dispose()
        {
            _ctx.Dispose();
        }
 
        private static void Check(int result)
        {
            const int Success = 1;
 
            if (result != Success)
            {
                Debug.Assert(result == 0);
                throw Interop.Crypto.CreateOpenSslCryptographicException();
            }
        }
    }
 
    internal readonly struct LiteHash : ILiteHash
    {
        private readonly SafeEvpMdCtxHandle _ctx;
        private readonly IntPtr _algorithm;
        private readonly int _hashSizeInBytes;
 
        public int HashSizeInBytes => _hashSizeInBytes;
 
        internal LiteHash(IntPtr algorithm)
        {
            Debug.Assert(algorithm != IntPtr.Zero);
 
            _algorithm = algorithm;
            _hashSizeInBytes = Interop.Crypto.EvpMdSize(algorithm);
 
            if (_hashSizeInBytes <= 0 || _hashSizeInBytes > Interop.Crypto.EVP_MAX_MD_SIZE)
            {
                Debug.Fail($"Unexpected hash '{_hashSizeInBytes}' size from {nameof(Interop.Crypto.EvpMdSize)}.");
                throw new CryptographicException();
            }
 
            _ctx = Interop.Crypto.EvpMdCtxCreate(algorithm);
            Interop.Crypto.CheckValidOpenSslHandle(_ctx);
        }
 
        private LiteHash(SafeEvpMdCtxHandle ctx, IntPtr algorithm, int hashSizeInBytes)
        {
            _ctx = ctx;
            _algorithm = algorithm;
            _hashSizeInBytes = hashSizeInBytes;
        }
 
        public void Append(ReadOnlySpan<byte> data)
        {
            if (data.IsEmpty)
            {
                return;
            }
 
            Check(Interop.Crypto.EvpDigestUpdate(_ctx, data, data.Length));
        }
 
        public int Finalize(Span<byte> destination)
        {
            Debug.Assert(destination.Length >= _hashSizeInBytes);
 
            uint length = (uint)destination.Length;
            Check(Interop.Crypto.EvpDigestFinalEx(_ctx, ref MemoryMarshal.GetReference(destination), ref length));
 
            Debug.Assert(length == _hashSizeInBytes);
            return _hashSizeInBytes;
        }
 
        public void Reset()
        {
            Check(Interop.Crypto.EvpDigestReset(_ctx, _algorithm));
        }
 
        public int Current(Span<byte> destination)
        {
            uint length = (uint)destination.Length;
            Check(Interop.Crypto.EvpDigestCurrent(_ctx, ref MemoryMarshal.GetReference(destination), ref length));
            Debug.Assert(length == _hashSizeInBytes);
            return _hashSizeInBytes;
        }
 
        public LiteHash Clone()
        {
            SafeEvpMdCtxHandle clone = Interop.Crypto.EvpMdCtxCopyEx(_ctx);
            Interop.Crypto.CheckValidOpenSslHandle(clone);
            return new LiteHash(clone, _algorithm, _hashSizeInBytes);
        }
 
        public void Dispose()
        {
            _ctx.Dispose();
        }
 
        private static void Check(int result)
        {
            const int Success = 1;
 
            if (result != Success)
            {
                Debug.Assert(result == 0);
                throw Interop.Crypto.CreateOpenSslCryptographicException();
            }
        }
    }
 
    internal readonly struct LiteHmac : ILiteHash
    {
        private readonly SafeHmacCtxHandle _ctx;
        private readonly int _hashSizeInBytes;
 
        public int HashSizeInBytes => _hashSizeInBytes;
 
        internal LiteHmac(IntPtr algorithm, ReadOnlySpan<byte> key)
        {
            Debug.Assert(algorithm != IntPtr.Zero);
            _hashSizeInBytes = Interop.Crypto.EvpMdSize(algorithm);
 
            if (_hashSizeInBytes <= 0 || _hashSizeInBytes > Interop.Crypto.EVP_MAX_MD_SIZE)
            {
                Debug.Fail($"Unexpected hash '{_hashSizeInBytes}' size from {nameof(Interop.Crypto.EvpMdSize)}.");
                throw new CryptographicException();
            }
 
            _ctx = Interop.Crypto.HmacCreate(ref MemoryMarshal.GetReference(key), key.Length, algorithm);
            Interop.Crypto.CheckValidOpenSslHandle(_ctx);
        }
 
        private LiteHmac(SafeHmacCtxHandle ctx, int hashSizeInBytes)
        {
            _ctx = ctx;
            _hashSizeInBytes = hashSizeInBytes;
        }
 
        public void Append(ReadOnlySpan<byte> data)
        {
            if (data.IsEmpty)
            {
                return;
            }
 
            Check(Interop.Crypto.HmacUpdate(_ctx, data, data.Length));
        }
 
        public int Current(Span<byte> destination)
        {
            Debug.Assert(destination.Length >= _hashSizeInBytes);
 
            int length = destination.Length;
            Check(Interop.Crypto.HmacCurrent(_ctx, ref MemoryMarshal.GetReference(destination), ref length));
            Debug.Assert(length == _hashSizeInBytes);
            return _hashSizeInBytes;
        }
 
        public int Finalize(Span<byte> destination)
        {
            Debug.Assert(destination.Length >= _hashSizeInBytes);
 
            int length = destination.Length;
            Check(Interop.Crypto.HmacFinal(_ctx, ref MemoryMarshal.GetReference(destination), ref length));
            Debug.Assert(length == _hashSizeInBytes);
            return _hashSizeInBytes;
        }
 
        public void Reset()
        {
            Check(Interop.Crypto.HmacReset(_ctx));
        }
 
        public LiteHmac Clone()
        {
            SafeHmacCtxHandle clone = Interop.Crypto.HmacCopy(_ctx);
            Interop.Crypto.CheckValidOpenSslHandle(clone);
            return new LiteHmac(clone, _hashSizeInBytes);
        }
 
        public void Dispose()
        {
            _ctx.Dispose();
        }
 
        private static void Check(int result)
        {
            const int Success = 1;
 
            if (result != Success)
            {
                Debug.Assert(result == 0);
                throw Interop.Crypto.CreateOpenSslCryptographicException();
            }
        }
    }
}