using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography;
using MediaBrowser.Model.Cryptography;
using static MediaBrowser.Model.Cryptography.Constants;
namespace Emby.Server.Implementations.Cryptography
{
///
/// Class providing abstractions over cryptographic functions.
///
public class CryptographyProvider : ICryptoProvider
{
///
public string DefaultHashMethod => "PBKDF2-SHA512";
///
public PasswordHash CreatePasswordHash(ReadOnlySpan password)
{
byte[] salt = GenerateSalt();
return new PasswordHash(
DefaultHashMethod,
Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
DefaultIterations,
HashAlgorithmName.SHA512,
DefaultOutputLength),
salt,
new Dictionary
{
{ "iterations", DefaultIterations.ToString(CultureInfo.InvariantCulture) }
});
}
///
public bool Verify(PasswordHash hash, ReadOnlySpan 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));
}
throw new NotSupportedException($"Can't verify hash with id: {hash.Id}");
}
///
public byte[] GenerateSalt()
=> GenerateSalt(DefaultSaltLength);
///
public byte[] GenerateSalt(int length)
{
var salt = new byte[length];
using var rng = RandomNumberGenerator.Create();
rng.GetNonZeroBytes(salt);
return salt;
}
}
}