aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
blob: 5380c45d84806af1f17e34620fdc9dbf49cf9961 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
{
    /// <summary>
    /// Class providing abstractions over cryptographic functions.
    /// </summary>
    public class CryptographyProvider : ICryptoProvider
    {
        /// <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));
            }

            throw new NotSupportedException($"Can't verify hash with id: {hash.Id}");
        }

        /// <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;
        }
    }
}