| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 | using System;using System.Collections.Generic;using System.Globalization;using System.Security.Cryptography;using System.Text;using MediaBrowser.Common.Extensions;using MediaBrowser.Model.Cryptography;using static MediaBrowser.Model.Cryptography.Constants;namespace Emby.Server.Implementations.Cryptography{    /// <summary>    /// Class providing abstractions over cryptographic functions.    /// </summary>    public class CryptographyProvider : ICryptoProvider    {        // TODO: remove when not needed for backwards compat        private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()            {                "MD5",                "System.Security.Cryptography.MD5",                "SHA",                "SHA1",                "System.Security.Cryptography.SHA1",                "SHA256",                "SHA-256",                "System.Security.Cryptography.SHA256",                "SHA384",                "SHA-384",                "System.Security.Cryptography.SHA384",                "SHA512",                "SHA-512",                "System.Security.Cryptography.SHA512"            };        /// <inheritdoc />        public string DefaultHashMethod => "PBKDF2-SHA512";        /// <inheritdoc />        public PasswordHash CreatePasswordHash(ReadOnlySpan<char> password)        {            byte[] salt = GenerateSalt();            return new PasswordHash(                DefaultHashMethod,                Rfc2898DeriveBytes.Pbkdf2(                    password,                    salt,                    DefaultIterations,                    HashAlgorithmName.SHA512,                    DefaultOutputLength),                salt,                new Dictionary<string, string>                {                    { "iterations", DefaultIterations.ToString(CultureInfo.InvariantCulture) }                });        }        /// <inheritdoc />        public bool Verify(PasswordHash hash, ReadOnlySpan<char> password)        {            if (string.Equals(hash.Id, "PBKDF2", StringComparison.Ordinal))            {                return hash.Hash.SequenceEqual(                    Rfc2898DeriveBytes.Pbkdf2(                        password,                        hash.Salt,                        int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture),                        HashAlgorithmName.SHA1,                        32));            }            if (string.Equals(hash.Id, "PBKDF2-SHA512", StringComparison.Ordinal))            {                return hash.Hash.SequenceEqual(                    Rfc2898DeriveBytes.Pbkdf2(                        password,                        hash.Salt,                        int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture),                        HashAlgorithmName.SHA512,                        DefaultOutputLength));            }            if (!_supportedHashMethods.Contains(hash.Id))            {                throw new CryptographicException($"Requested hash method is not supported: {hash.Id}");            }            using var h = HashAlgorithm.Create(hash.Id) ?? throw new ResourceNotFoundException($"Unknown hash method: {hash.Id}.");            var bytes = Encoding.UTF8.GetBytes(password.ToArray());            if (hash.Salt.Length == 0)            {                return hash.Hash.SequenceEqual(h.ComputeHash(bytes));            }            byte[] salted = new byte[bytes.Length + hash.Salt.Length];            Array.Copy(bytes, salted, bytes.Length);            hash.Salt.CopyTo(salted.AsSpan(bytes.Length));            return hash.Hash.SequenceEqual(h.ComputeHash(salted));        }        /// <inheritdoc />        public byte[] GenerateSalt()            => GenerateSalt(DefaultSaltLength);        /// <inheritdoc />        public byte[] GenerateSalt(int length)        {            var salt = new byte[length];            using var rng = RandomNumberGenerator.Create();            rng.GetNonZeroBytes(salt);            return salt;        }    }}
 |