CryptographyProvider.cs 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Security.Cryptography;
  4. using MediaBrowser.Common.Extensions;
  5. using MediaBrowser.Model.Cryptography;
  6. using static MediaBrowser.Common.Cryptography.Constants;
  7. namespace Emby.Server.Implementations.Cryptography
  8. {
  9. /// <summary>
  10. /// Class providing abstractions over cryptographic functions.
  11. /// </summary>
  12. public class CryptographyProvider : ICryptoProvider
  13. {
  14. // FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto
  15. // Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1
  16. // there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one
  17. // Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1
  18. private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
  19. {
  20. "MD5",
  21. "System.Security.Cryptography.MD5",
  22. "SHA",
  23. "SHA1",
  24. "System.Security.Cryptography.SHA1",
  25. "SHA256",
  26. "SHA-256",
  27. "System.Security.Cryptography.SHA256",
  28. "SHA384",
  29. "SHA-384",
  30. "System.Security.Cryptography.SHA384",
  31. "SHA512",
  32. "SHA-512",
  33. "System.Security.Cryptography.SHA512"
  34. };
  35. /// <inheritdoc />
  36. public string DefaultHashMethod => "PBKDF2";
  37. /// <inheritdoc />
  38. public IEnumerable<string> GetSupportedHashMethods()
  39. => _supportedHashMethods;
  40. private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
  41. {
  42. // downgrading for now as we need this library to be dotnetstandard compliant
  43. // with this downgrade we'll add a check to make sure we're on the downgrade method at the moment
  44. if (method != DefaultHashMethod)
  45. {
  46. throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}");
  47. }
  48. using var r = new Rfc2898DeriveBytes(bytes, salt, iterations);
  49. return r.GetBytes(32);
  50. }
  51. /// <inheritdoc />
  52. public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt)
  53. {
  54. if (hashMethod == DefaultHashMethod)
  55. {
  56. return PBKDF2(hashMethod, bytes, salt, DefaultIterations);
  57. }
  58. if (!_supportedHashMethods.Contains(hashMethod))
  59. {
  60. throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
  61. }
  62. using var h = HashAlgorithm.Create(hashMethod) ?? throw new ResourceNotFoundException($"Unknown hash method: {hashMethod}.");
  63. if (salt.Length == 0)
  64. {
  65. return h.ComputeHash(bytes);
  66. }
  67. byte[] salted = new byte[bytes.Length + salt.Length];
  68. Array.Copy(bytes, salted, bytes.Length);
  69. Array.Copy(salt, 0, salted, bytes.Length, salt.Length);
  70. return h.ComputeHash(salted);
  71. }
  72. /// <inheritdoc />
  73. public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
  74. => PBKDF2(DefaultHashMethod, bytes, salt, DefaultIterations);
  75. /// <inheritdoc />
  76. public byte[] GenerateSalt()
  77. => GenerateSalt(DefaultSaltLength);
  78. /// <inheritdoc />
  79. public byte[] GenerateSalt(int length)
  80. => RandomNumberGenerator.GetBytes(length);
  81. }
  82. }