aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Common/Cryptography/PasswordHash.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Common/Cryptography/PasswordHash.cs')
-rw-r--r--MediaBrowser.Common/Cryptography/PasswordHash.cs155
1 files changed, 155 insertions, 0 deletions
diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs
new file mode 100644
index 000000000..5b28d344f
--- /dev/null
+++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using static MediaBrowser.Common.HexHelper;
+
+namespace MediaBrowser.Common.Cryptography
+{
+ // Defined from this hash storage spec
+ // https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md
+ // $<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
+ // with one slight amendment to ease the transition, we're writing out the bytes in hex
+ // rather than making them a BASE64 string with stripped padding
+ public class PasswordHash
+ {
+ private readonly Dictionary<string, string> _parameters;
+
+ public PasswordHash(string id, byte[] hash)
+ : this(id, hash, Array.Empty<byte>())
+ {
+
+ }
+
+ public PasswordHash(string id, byte[] hash, byte[] salt)
+ : this(id, hash, salt, new Dictionary<string, string>())
+ {
+
+ }
+
+ public PasswordHash(string id, byte[] hash, byte[] salt, Dictionary<string, string> parameters)
+ {
+ Id = id;
+ Hash = hash;
+ Salt = salt;
+ _parameters = parameters;
+ }
+
+ /// <summary>
+ /// Gets the symbolic name for the function used.
+ /// </summary>
+ /// <value>Returns the symbolic name for the function used.</value>
+ public string Id { get; }
+
+ /// <summary>
+ /// Gets the additional parameters used by the hash function.
+ /// </summary>
+ /// <value></value>
+ public IReadOnlyDictionary<string, string> Parameters => _parameters;
+
+ /// <summary>
+ /// Gets the salt used for hashing the password.
+ /// </summary>
+ /// <value>Returns the salt used for hashing the password.</value>
+ public byte[] Salt { get; }
+
+ /// <summary>
+ /// Gets the hashed password.
+ /// </summary>
+ /// <value>Return the hashed password.</value>
+ public byte[] Hash { get; }
+
+ public static PasswordHash Parse(string storageString)
+ {
+ string[] splitted = storageString.Split('$');
+ // The string should at least contain the hash function and the hash itself
+ if (splitted.Length < 3)
+ {
+ throw new ArgumentException("String doesn't contain enough segments", nameof(storageString));
+ }
+
+ // Start at 1, the first index shouldn't contain any data
+ int index = 1;
+
+ // Name of the hash function
+ string id = splitted[index++];
+
+ // Optional parameters
+ Dictionary<string, string> parameters = new Dictionary<string, string>();
+ if (splitted[index].IndexOf('=') != -1)
+ {
+ foreach (string paramset in splitted[index++].Split(','))
+ {
+ if (string.IsNullOrEmpty(paramset))
+ {
+ continue;
+ }
+
+ string[] fields = paramset.Split('=');
+ if (fields.Length != 2)
+ {
+ throw new InvalidDataException($"Malformed parameter in password hash string {paramset}");
+ }
+
+ parameters.Add(fields[0], fields[1]);
+ }
+ }
+
+ byte[] hash;
+ byte[] salt;
+ // Check if the string also contains a salt
+ if (splitted.Length - index == 2)
+ {
+ salt = FromHexString(splitted[index++]);
+ hash = FromHexString(splitted[index++]);
+ }
+ else
+ {
+ salt = Array.Empty<byte>();
+ hash = FromHexString(splitted[index++]);
+ }
+
+ return new PasswordHash(id, hash, salt, parameters);
+ }
+
+ private void SerializeParameters(StringBuilder stringBuilder)
+ {
+ if (_parameters.Count == 0)
+ {
+ return;
+ }
+
+ stringBuilder.Append('$');
+ foreach (var pair in _parameters)
+ {
+ stringBuilder.Append(pair.Key);
+ stringBuilder.Append('=');
+ stringBuilder.Append(pair.Value);
+ stringBuilder.Append(',');
+ }
+
+ // Remove last ','
+ stringBuilder.Length -= 1;
+ }
+
+ /// <inheritdoc />
+ public override string ToString()
+ {
+ var str = new StringBuilder();
+ str.Append('$');
+ str.Append(Id);
+ SerializeParameters(str);
+
+ if (Salt.Length != 0)
+ {
+ str.Append('$');
+ str.Append(ToHexString(Salt));
+ }
+
+ str.Append('$');
+ str.Append(ToHexString(Hash));
+
+ return str.ToString();
+ }
+ }
+}