From 8c8a71692e2f42e30f95e9ff38cc5442a0d56978 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 25 Nov 2020 23:40:31 +0100 Subject: [PATCH 1/2] Remove Hex class as the BCL has one now --- .../LiveTv/Listings/SchedulesDirect.cs | 2 +- .../QuickConnect/QuickConnectManager.cs | 2 +- .../Updates/InstallationManager.cs | 2 +- Jellyfin.Api/Controllers/LiveTvController.cs | 5 +- .../Cryptography/PasswordHash.cs | 10 +- MediaBrowser.Common/Hex.cs | 94 ------------------- .../HexDecodeBenches.cs | 45 --------- .../HexEncodeBenches.cs | 32 ------- .../Jellyfin.Common.Benches.csproj | 16 ---- benches/Jellyfin.Common.Benches/Program.cs | 14 --- tests/Jellyfin.Common.Tests/HexTests.cs | 19 ---- .../PasswordHashTests.cs | 5 +- 12 files changed, 13 insertions(+), 233 deletions(-) delete mode 100644 MediaBrowser.Common/Hex.cs delete mode 100644 benches/Jellyfin.Common.Benches/HexDecodeBenches.cs delete mode 100644 benches/Jellyfin.Common.Benches/HexEncodeBenches.cs delete mode 100644 benches/Jellyfin.Common.Benches/Jellyfin.Common.Benches.csproj delete mode 100644 benches/Jellyfin.Common.Benches/Program.cs delete mode 100644 tests/Jellyfin.Common.Tests/HexTests.cs diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 5d17ba1de9..8c44b244c3 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -647,7 +647,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/token"); var hashedPasswordBytes = _cryptoProvider.ComputeHash("SHA1", Encoding.ASCII.GetBytes(password), Array.Empty()); - string hashedPassword = Hex.Encode(hashedPasswordBytes); + string hashedPassword = Convert.ToHexString(hashedPasswordBytes); options.Content = new StringContent("{\"username\":\"" + username + "\",\"password\":\"" + hashedPassword + "\"}", Encoding.UTF8, MediaTypeNames.Application.Json); using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs index 140a675414..7bed06de36 100644 --- a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs +++ b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs @@ -243,7 +243,7 @@ namespace Emby.Server.Implementations.QuickConnect Span bytes = stackalloc byte[length]; _rng.GetBytes(bytes); - return Hex.Encode(bytes); + return Convert.ToHexString(bytes); } /// diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index f2c096b8af..ef346dd5d6 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -414,7 +414,7 @@ namespace Emby.Server.Implementations.Updates using var md5 = MD5.Create(); cancellationToken.ThrowIfCancellationRequested(); - var hash = Hex.Encode(md5.ComputeHash(stream)); + var hash = Convert.ToHexString(md5.ComputeHash(stream)); if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase)) { _logger.LogError( diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 410f3a3400..2c27601e94 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; @@ -17,7 +17,6 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.LiveTvDtos; using Jellyfin.Data.Enums; -using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Dto; @@ -1015,7 +1014,7 @@ namespace Jellyfin.Api.Controllers if (!string.IsNullOrEmpty(pw)) { using var sha = SHA1.Create(); - listingsProviderInfo.Password = Hex.Encode(sha.ComputeHash(Encoding.UTF8.GetBytes(pw))); + listingsProviderInfo.Password = Convert.ToHexString(sha.ComputeHash(Encoding.UTF8.GetBytes(pw))); } return await _liveTvManager.SaveListingProvider(listingsProviderInfo, validateLogin, validateListings).ConfigureAwait(false); diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs index 3e12536ec7..3e2eae1c8e 100644 --- a/MediaBrowser.Common/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs @@ -101,13 +101,13 @@ namespace MediaBrowser.Common.Cryptography // Check if the string also contains a salt if (splitted.Length - index == 2) { - salt = Hex.Decode(splitted[index++]); - hash = Hex.Decode(splitted[index++]); + salt = Convert.FromHexString(splitted[index++]); + hash = Convert.FromHexString(splitted[index++]); } else { salt = Array.Empty(); - hash = Hex.Decode(splitted[index++]); + hash = Convert.FromHexString(splitted[index++]); } return new PasswordHash(id, hash, salt, parameters); @@ -144,11 +144,11 @@ namespace MediaBrowser.Common.Cryptography if (_salt.Length != 0) { str.Append('$') - .Append(Hex.Encode(_salt, false)); + .Append(Convert.ToHexString(_salt)); } return str.Append('$') - .Append(Hex.Encode(_hash, false)).ToString(); + .Append(Convert.ToHexString(_hash)).ToString(); } } } diff --git a/MediaBrowser.Common/Hex.cs b/MediaBrowser.Common/Hex.cs deleted file mode 100644 index 559109f74f..0000000000 --- a/MediaBrowser.Common/Hex.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace MediaBrowser.Common -{ - /// - /// Encoding and decoding hex strings. - /// - public static class Hex - { - internal const string HexCharsLower = "0123456789abcdef"; - internal const string HexCharsUpper = "0123456789ABCDEF"; - - internal const int LastHexSymbol = 0x66; // 102: f - - /// - /// Gets a map from an ASCII char to its hex value shifted, - /// e.g. b -> 11. 0xFF means it's not a hex symbol. - /// - internal static ReadOnlySpan HexLookup => new byte[] - { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f - }; - - /// - /// Encodes each element of the specified bytes as its hexadecimal string representation. - /// - /// An array of bytes. - /// true to use lowercase hexadecimal characters; otherwise false. - /// bytes as a hex string. - public static string Encode(ReadOnlySpan bytes, bool lowercase = true) - { - var hexChars = lowercase ? HexCharsLower : HexCharsUpper; - - // TODO: use string.Create when it's supports spans - // Ref: https://github.com/dotnet/corefx/issues/29120 - char[] s = new char[bytes.Length * 2]; - int j = 0; - for (int i = 0; i < bytes.Length; i++) - { - s[j++] = hexChars[bytes[i] >> 4]; - s[j++] = hexChars[bytes[i] & 0x0f]; - } - - return new string(s); - } - - /// - /// Decodes a hex string into bytes. - /// - /// The . - /// The decoded bytes. - public static byte[] Decode(ReadOnlySpan str) - { - if (str.Length == 0) - { - return Array.Empty(); - } - - var unHex = HexLookup; - - int byteLen = str.Length / 2; - byte[] bytes = new byte[byteLen]; - int i = 0; - for (int j = 0; j < byteLen; j++) - { - byte a; - byte b; - if (str[i] > LastHexSymbol - || (a = unHex[str[i++]]) == 0xFF - || str[i] > LastHexSymbol - || (b = unHex[str[i++]]) == 0xFF) - { - ThrowArgumentException(nameof(str)); - break; // Unreachable - } - - bytes[j] = (byte)((a * 16) | b); - } - - return bytes; - } - - [DoesNotReturn] - private static void ThrowArgumentException(string paramName) - => throw new ArgumentException("Character is not a hex symbol.", paramName); - } -} diff --git a/benches/Jellyfin.Common.Benches/HexDecodeBenches.cs b/benches/Jellyfin.Common.Benches/HexDecodeBenches.cs deleted file mode 100644 index d9a107b696..0000000000 --- a/benches/Jellyfin.Common.Benches/HexDecodeBenches.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Globalization; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using MediaBrowser.Common; - -namespace Jellyfin.Common.Benches -{ - [MemoryDiagnoser] - public class HexDecodeBenches - { - private string _data; - - [Params(0, 10, 100, 1000, 10000, 1000000)] - public int N { get; set; } - - [GlobalSetup] - public void GlobalSetup() - { - var bytes = new byte[N]; - new Random(42).NextBytes(bytes); - _data = Hex.Encode(bytes); - } - - [Benchmark] - public byte[] Decode() => Hex.Decode(_data); - - [Benchmark] - public byte[] DecodeSubString() => DecodeSubString(_data); - - private static byte[] DecodeSubString(string str) - { - byte[] bytes = new byte[str.Length / 2]; - for (int i = 0; i < str.Length; i += 2) - { - bytes[i / 2] = byte.Parse( - str.Substring(i, 2), - NumberStyles.HexNumber, - CultureInfo.InvariantCulture); - } - - return bytes; - } - } -} diff --git a/benches/Jellyfin.Common.Benches/HexEncodeBenches.cs b/benches/Jellyfin.Common.Benches/HexEncodeBenches.cs deleted file mode 100644 index 7abf93c510..0000000000 --- a/benches/Jellyfin.Common.Benches/HexEncodeBenches.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; -using MediaBrowser.Common; - -namespace Jellyfin.Common.Benches -{ - [MemoryDiagnoser] - public class HexEncodeBenches - { - private byte[] _data; - - [Params(0, 10, 100, 1000, 10000, 1000000)] - public int N { get; set; } - - [GlobalSetup] - public void GlobalSetup() - { - _data = new byte[N]; - new Random(42).NextBytes(_data); - } - - [Benchmark] - public string HexEncode() => Hex.Encode(_data); - - [Benchmark] - public string BitConverterToString() => BitConverter.ToString(_data); - - [Benchmark] - public string BitConverterToStringWithReplace() => BitConverter.ToString(_data).Replace("-", ""); - } -} diff --git a/benches/Jellyfin.Common.Benches/Jellyfin.Common.Benches.csproj b/benches/Jellyfin.Common.Benches/Jellyfin.Common.Benches.csproj deleted file mode 100644 index c564e86e9e..0000000000 --- a/benches/Jellyfin.Common.Benches/Jellyfin.Common.Benches.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net5.0 - - - - - - - - - - - diff --git a/benches/Jellyfin.Common.Benches/Program.cs b/benches/Jellyfin.Common.Benches/Program.cs deleted file mode 100644 index b218b0dc10..0000000000 --- a/benches/Jellyfin.Common.Benches/Program.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using BenchmarkDotNet.Running; - -namespace Jellyfin.Common.Benches -{ - public static class Program - { - public static void Main(string[] args) - { - _ = BenchmarkRunner.Run(); - _ = BenchmarkRunner.Run(); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/HexTests.cs b/tests/Jellyfin.Common.Tests/HexTests.cs deleted file mode 100644 index 5b578d38cb..0000000000 --- a/tests/Jellyfin.Common.Tests/HexTests.cs +++ /dev/null @@ -1,19 +0,0 @@ -using MediaBrowser.Common; -using Xunit; - -namespace Jellyfin.Common.Tests -{ - public class HexTests - { - [Theory] - [InlineData("")] - [InlineData("00")] - [InlineData("01")] - [InlineData("000102030405060708090a0b0c0d0e0f")] - [InlineData("0123456789abcdef")] - public void RoundTripTest(string data) - { - Assert.Equal(data, Hex.Encode(Hex.Decode(data))); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/PasswordHashTests.cs b/tests/Jellyfin.Common.Tests/PasswordHashTests.cs index 46926f4f81..c4422bd105 100644 --- a/tests/Jellyfin.Common.Tests/PasswordHashTests.cs +++ b/tests/Jellyfin.Common.Tests/PasswordHashTests.cs @@ -1,3 +1,4 @@ +using System; using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using Xunit; @@ -16,8 +17,8 @@ namespace Jellyfin.Common.Tests { var pass = PasswordHash.Parse(passwordHash); Assert.Equal(id, pass.Id); - Assert.Equal(salt, Hex.Encode(pass.Salt, false)); - Assert.Equal(hash, Hex.Encode(pass.Hash, false)); + Assert.Equal(salt, Convert.ToHexString(pass.Salt)); + Assert.Equal(hash, Convert.ToHexString(pass.Hash)); } [Theory] From 38932fc7d1c6851b6a282e9723bea471ef494b96 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 26 Nov 2020 13:21:04 +0100 Subject: [PATCH 2/2] Schedules Direct requires the hex to be lowercase --- .../LiveTv/Listings/SchedulesDirect.cs | 4 +++- Jellyfin.Api/Controllers/LiveTvController.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 8c44b244c3..f181eb7a0b 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -647,7 +647,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings { using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/token"); var hashedPasswordBytes = _cryptoProvider.ComputeHash("SHA1", Encoding.ASCII.GetBytes(password), Array.Empty()); - string hashedPassword = Convert.ToHexString(hashedPasswordBytes); + // TODO: remove ToLower when Convert.ToHexString supports lowercase + // Schedules Direct requires the hex to be lowercase + string hashedPassword = Convert.ToHexString(hashedPasswordBytes).ToLowerInvariant(); options.Content = new StringContent("{\"username\":\"" + username + "\",\"password\":\"" + hashedPassword + "\"}", Encoding.UTF8, MediaTypeNames.Application.Json); using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 2c27601e94..56d4b3933e 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1014,7 +1014,9 @@ namespace Jellyfin.Api.Controllers if (!string.IsNullOrEmpty(pw)) { using var sha = SHA1.Create(); - listingsProviderInfo.Password = Convert.ToHexString(sha.ComputeHash(Encoding.UTF8.GetBytes(pw))); + // TODO: remove ToLower when Convert.ToHexString supports lowercase + // Schedules Direct requires the hex to be lowercase + listingsProviderInfo.Password = Convert.ToHexString(sha.ComputeHash(Encoding.UTF8.GetBytes(pw))).ToLowerInvariant(); } return await _liveTvManager.SaveListingProvider(listingsProviderInfo, validateLogin, validateListings).ConfigureAwait(false);