diff options
| author | Bond-009 <bond.009@outlook.com> | 2019-09-17 18:07:15 +0200 |
|---|---|---|
| committer | Anthony Lavado <anthony@lavado.ca> | 2019-09-17 12:07:15 -0400 |
| commit | 6f17a0b7af5775386e554f2e2e2a4a6829d2895d (patch) | |
| tree | ce792d21af0f8e5d0208aec1aba55e8047f2f439 /MediaBrowser.Common/Cryptography/PasswordHash.cs | |
| parent | adc2a68a98a572e6541ffac587fd9f6247aec6d5 (diff) | |
Remove legacy auth code (#1677)
* Remove legacy auth code
* Adds tests so we don't break PasswordHash (again)
* Clean up interfaces
* Remove duplicate code
* Use auto properties
* static using
* Don't use 'this'
* Fix build
Diffstat (limited to 'MediaBrowser.Common/Cryptography/PasswordHash.cs')
| -rw-r--r-- | MediaBrowser.Common/Cryptography/PasswordHash.cs | 155 |
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 0000000000..5b28d344f6 --- /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(); + } + } +} |
