From 1e4f0d9e1b01c66817e6898832263d4c3c15eb63 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 6 Apr 2017 21:49:44 +0100 Subject: [PATCH] revert --- Ombi.Core.Migration/Migrations/Version2210.cs | 2 +- Ombi.Services/Jobs/PlexContentCacher.cs | 9 +- .../EmbyRecentlyAddedNewsletter.cs | 4 +- .../PlexRecentlyAddedNewsletter.cs | 4 +- Ombi/Ombi.Api/Ombi.Api.csproj | 2 +- Ombi/Ombi.Core/Ombi.Core.csproj | 2 +- Ombi/Ombi.Core/Settings/ISettingsService.cs | 4 +- .../Ombi.DependencyInjection.csproj | 2 +- .../Ombi.Helpers.Tests.csproj | 16 --- Ombi/Ombi.Helpers.Tests/StringCipherTests.cs | 53 --------- Ombi/Ombi.Helpers/Ombi.Helpers.csproj | 2 +- Ombi/Ombi.Helpers/StringCipher.cs | 109 ++++++++++++------ Ombi/Ombi.Store/Ombi.Store.csproj | 2 +- .../Ombi.TheMovieDbApi.csproj | 3 +- Ombi/Ombi.sln | 11 +- 15 files changed, 92 insertions(+), 133 deletions(-) delete mode 100644 Ombi/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj delete mode 100644 Ombi/Ombi.Helpers.Tests/StringCipherTests.cs diff --git a/Ombi.Core.Migration/Migrations/Version2210.cs b/Ombi.Core.Migration/Migrations/Version2210.cs index 258e4da46..94592174a 100644 --- a/Ombi.Core.Migration/Migrations/Version2210.cs +++ b/Ombi.Core.Migration/Migrations/Version2210.cs @@ -53,7 +53,7 @@ namespace Ombi.Core.Migration.Migrations EmbyEpisodes = embyEp; } - public int Version => 22000; + public int Version => 22100; private IRepository Log { get; } private IRepository PlexContent { get; } private IRepository PlexEpisodes { get; } diff --git a/Ombi.Services/Jobs/PlexContentCacher.cs b/Ombi.Services/Jobs/PlexContentCacher.cs index 5b6dc55d4..d4f872fc2 100644 --- a/Ombi.Services/Jobs/PlexContentCacher.cs +++ b/Ombi.Services/Jobs/PlexContentCacher.cs @@ -276,7 +276,8 @@ namespace Ombi.Services.Jobs Title = m.Title, Type = Store.Models.Plex.PlexMediaType.Movie, Url = m.Url, - ItemId = m.ItemId + ItemId = m.ItemId, + AddedAt = DateTime.UtcNow, }); } } @@ -318,7 +319,8 @@ namespace Ombi.Services.Jobs Type = Store.Models.Plex.PlexMediaType.Show, Url = t.Url, Seasons = ByteConverterHelper.ReturnBytes(t.Seasons), - ItemId = t.ItemId + ItemId = t.ItemId, + AddedAt = DateTime.UtcNow, }); } } @@ -360,7 +362,8 @@ namespace Ombi.Services.Jobs Title = a.Title, Type = Store.Models.Plex.PlexMediaType.Artist, Url = a.Url, - ItemId = "album" + ItemId = "album", + AddedAt = DateTime.UtcNow, }); } } diff --git a/Ombi.Services/Jobs/RecentlyAddedNewsletter/EmbyRecentlyAddedNewsletter.cs b/Ombi.Services/Jobs/RecentlyAddedNewsletter/EmbyRecentlyAddedNewsletter.cs index c4506e843..158d77f0d 100644 --- a/Ombi.Services/Jobs/RecentlyAddedNewsletter/EmbyRecentlyAddedNewsletter.cs +++ b/Ombi.Services/Jobs/RecentlyAddedNewsletter/EmbyRecentlyAddedNewsletter.cs @@ -120,7 +120,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter var filteredSeries = series.Where(m => recentlyAdded.All(x => x.ProviderId != m.EmbyId)).ToList(); var info = new List(); - foreach (var m in filteredMovies) + foreach (var m in filteredMovies.OrderByDescending(x => x.AddedAt)) { var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception thrown when processing an emby movie for the newsletter, Retrying {0}", timespan)); @@ -210,7 +210,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter } else { - foreach (var t in filteredSeries) + foreach (var t in filteredSeries.OrderByDescending(x => x.AddedAt)) { diff --git a/Ombi.Services/Jobs/RecentlyAddedNewsletter/PlexRecentlyAddedNewsletter.cs b/Ombi.Services/Jobs/RecentlyAddedNewsletter/PlexRecentlyAddedNewsletter.cs index 6eda44bec..ca88b4c2b 100644 --- a/Ombi.Services/Jobs/RecentlyAddedNewsletter/PlexRecentlyAddedNewsletter.cs +++ b/Ombi.Services/Jobs/RecentlyAddedNewsletter/PlexRecentlyAddedNewsletter.cs @@ -127,7 +127,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter // if this is a test make sure we show something filteredMovies = movie.Take(5).ToList(); } - foreach (var m in filteredMovies) + foreach (var m in filteredMovies.OrderByDescending(x => x.AddedAt)) { var i = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, m.ItemId); if (i.Video == null) @@ -194,7 +194,7 @@ namespace Ombi.Services.Jobs.RecentlyAddedNewsletter // if this is a test make sure we show something filteredSeries = series.Take(5).ToList(); } - foreach (var t in filteredSeries) + foreach (var t in filteredSeries.OrderByDescending(x => x.AddedAt)) { var i = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, t.ItemId); if (i.Directory == null) diff --git a/Ombi/Ombi.Api/Ombi.Api.csproj b/Ombi/Ombi.Api/Ombi.Api.csproj index b3d86c156..80457725a 100644 --- a/Ombi/Ombi.Api/Ombi.Api.csproj +++ b/Ombi/Ombi.Api/Ombi.Api.csproj @@ -1,7 +1,7 @@  - netstandard1.6 + netstandard1.4 diff --git a/Ombi/Ombi.Core/Ombi.Core.csproj b/Ombi/Ombi.Core/Ombi.Core.csproj index ed719e074..078f56b95 100644 --- a/Ombi/Ombi.Core/Ombi.Core.csproj +++ b/Ombi/Ombi.Core/Ombi.Core.csproj @@ -1,7 +1,7 @@  - netstandard1.6 + netstandard1.4 diff --git a/Ombi/Ombi.Core/Settings/ISettingsService.cs b/Ombi/Ombi.Core/Settings/ISettingsService.cs index 7fe6b4f6f..f781676a0 100644 --- a/Ombi/Ombi.Core/Settings/ISettingsService.cs +++ b/Ombi/Ombi.Core/Settings/ISettingsService.cs @@ -8,7 +8,7 @@ namespace Ombi.Core.Settings Task GetSettingsAsync(); bool SaveSettings(T model); Task SaveSettingsAsync(T model); - void Delete(T model); - Task DeleteAsync(T model); + bool Delete(T model); + Task DeleteAsync(T model); } } \ No newline at end of file diff --git a/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 828d87026..0be698b01 100644 --- a/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -1,7 +1,7 @@  - netstandard1.6 + netstandard1.4 diff --git a/Ombi/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj b/Ombi/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj deleted file mode 100644 index c8047a6b6..000000000 --- a/Ombi/Ombi.Helpers.Tests/Ombi.Helpers.Tests.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netstandard1.6 - - - - - - - - - - - - \ No newline at end of file diff --git a/Ombi/Ombi.Helpers.Tests/StringCipherTests.cs b/Ombi/Ombi.Helpers.Tests/StringCipherTests.cs deleted file mode 100644 index 5866fbc88..000000000 --- a/Ombi/Ombi.Helpers.Tests/StringCipherTests.cs +++ /dev/null @@ -1,53 +0,0 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2017 Jamie Rees -// File: StringCipherTests.cs -// Created By: Jamie Rees -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// ************************************************************************/ -#endregion - -using NUnit.Framework; -using System.Collections.Generic; - -namespace Ombi.Helpers.Tests -{ - [TestFixture] - public class StringCipherTests - { - [TestCaseSource(nameof(StringData))] - public string AddPrefix(string text, string key) - { - return StringCipher.Encrypt(text, key); - } - - private static IEnumerable StringData - { - get - { - yield return new TestCaseData("password1","key1").Returns("AbcCba").SetName("pascalCase"); - yield return new TestCaseData("").Returns("").SetName("Empty"); - yield return new TestCaseData("12DSAda").Returns("12DSAda").SetName("With numbers"); - } - } - - } -} \ No newline at end of file diff --git a/Ombi/Ombi.Helpers/Ombi.Helpers.csproj b/Ombi/Ombi.Helpers/Ombi.Helpers.csproj index 2a9ec6330..498e443c1 100644 --- a/Ombi/Ombi.Helpers/Ombi.Helpers.csproj +++ b/Ombi/Ombi.Helpers/Ombi.Helpers.csproj @@ -1,7 +1,7 @@  - netstandard1.6 + netstandard1.4 diff --git a/Ombi/Ombi.Helpers/StringCipher.cs b/Ombi/Ombi.Helpers/StringCipher.cs index c8e75c588..152dc3f0d 100644 --- a/Ombi/Ombi.Helpers/StringCipher.cs +++ b/Ombi/Ombi.Helpers/StringCipher.cs @@ -1,12 +1,19 @@ using System; using System.IO; +using System.Linq; using System.Security.Cryptography; using System.Text; namespace Ombi.Helpers { - public static class StringCipher + public class StringCipher { + // This constant determines the number of iterations for the password bytes generation function. + private const int DerivationIterations = 1000; + // This constant is used to determine the keysize of the encryption algorithm in bits. + // We divide this by 8 within the code below to get the equivalent number of bytes. + private const int Keysize = 256; + /// /// Decrypts the specified cipher text. /// @@ -15,32 +22,39 @@ namespace Ombi.Helpers /// public static string Decrypt(string cipherText, string passPhrase) { - var fullCipher = Convert.FromBase64String(cipherText); - - var iv = new byte[16]; - var cipher = new byte[16]; - - Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length); - Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, iv.Length); - var key = Encoding.UTF8.GetBytes(passPhrase); + // Get the complete stream of bytes that represent: + // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText] + var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText); + // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes. + var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray(); + // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes. + var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray(); + // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string. + var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray(); - using (var aesAlg = Aes.Create()) + using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) { - using (var decryptor = aesAlg.CreateDecryptor(key, iv)) + var keyBytes = password.GetBytes(Keysize / 8); + var aes = Aes.Create(); + using (var symmetricKey = new RijndaelManaged()) { - string result; - using (var msDecrypt = new MemoryStream(cipher)) + symmetricKey.BlockSize = 256; + symmetricKey.Mode = CipherMode.CBC; + symmetricKey.Padding = PaddingMode.PKCS7; + using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes)) { - using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + using (var memoryStream = new MemoryStream(cipherTextBytes)) { - using (var srDecrypt = new StreamReader(csDecrypt)) + using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) { - result = srDecrypt.ReadToEnd(); + var plainTextBytes = new byte[cipherTextBytes.Length]; + var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); + memoryStream.Close(); + cryptoStream.Close(); + return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount); } } } - - return result; } } } @@ -53,33 +67,54 @@ namespace Ombi.Helpers /// public static string Encrypt(string plainText, string passPhrase) { - var key = Encoding.UTF8.GetBytes(passPhrase); - - using (var aesAlg = Aes.Create()) + // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text + // so that the same Salt and IV values can be used when decrypting. + var saltStringBytes = Generate256BitsOfRandomEntropy(); + var ivStringBytes = Generate256BitsOfRandomEntropy(); + var plainTextBytes = Encoding.UTF8.GetBytes(plainText); + using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) { - using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV)) + var keyBytes = password.GetBytes(Keysize / 8); + using (var symmetricKey = new RijndaelManaged()) { - using (var msEncrypt = new MemoryStream()) + symmetricKey.BlockSize = 256; + symmetricKey.Mode = CipherMode.CBC; + symmetricKey.Padding = PaddingMode.PKCS7; + using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes)) { - using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) - using (var swEncrypt = new StreamWriter(csEncrypt)) + using (var memoryStream = new MemoryStream()) { - swEncrypt.Write(plainText); + using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) + { + cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); + cryptoStream.FlushFinalBlock(); + // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes. + var cipherTextBytes = saltStringBytes; + cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray(); + cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray(); + memoryStream.Close(); + cryptoStream.Close(); + return Convert.ToBase64String(cipherTextBytes); + } } - - var iv = aesAlg.IV; - - var decryptedContent = msEncrypt.ToArray(); - - var result = new byte[iv.Length + decryptedContent.Length]; - - Buffer.BlockCopy(iv, 0, result, 0, iv.Length); - Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length); - - return Convert.ToBase64String(result); } } } } + + /// + /// Generate256s the bits of random entropy. + /// + /// + private static byte[] Generate256BitsOfRandomEntropy() + { + var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits. + using (var rngCsp = new RNGCryptoServiceProvider()) + { + // Fill the array with cryptographically secure random bytes. + rngCsp.GetBytes(randomBytes); + } + return randomBytes; + } } } \ No newline at end of file diff --git a/Ombi/Ombi.Store/Ombi.Store.csproj b/Ombi/Ombi.Store/Ombi.Store.csproj index 6dac2a1d5..7d9da619e 100644 --- a/Ombi/Ombi.Store/Ombi.Store.csproj +++ b/Ombi/Ombi.Store/Ombi.Store.csproj @@ -1,7 +1,7 @@  - netstandard1.6 + netstandard1.4 diff --git a/Ombi/Ombi.TheMovieDbApi/Ombi.TheMovieDbApi.csproj b/Ombi/Ombi.TheMovieDbApi/Ombi.TheMovieDbApi.csproj index 19b57c95c..16516740e 100644 --- a/Ombi/Ombi.TheMovieDbApi/Ombi.TheMovieDbApi.csproj +++ b/Ombi/Ombi.TheMovieDbApi/Ombi.TheMovieDbApi.csproj @@ -1,8 +1,7 @@  - netstandard1.6 - False + netstandard1.4 diff --git a/Ombi/Ombi.sln b/Ombi/Ombi.sln index cfe27f8fb..bd42c7e32 100644 --- a/Ombi/Ombi.sln +++ b/Ombi/Ombi.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.9 +VisualStudioVersion = 15.0.26228.10 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi", "Ombi\Ombi.csproj", "{C987AA67-AFE1-468F-ACD3-EAD5A48E1F6A}" EndProject @@ -26,10 +26,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Store", "Ombi.Store\Om EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.DependencyInjection", "Ombi.DependencyInjection\Ombi.DependencyInjection.csproj", "{B39E4558-C557-48E7-AA74-19C5CD809617}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Helpers.Tests", "Ombi.Helpers.Tests\Ombi.Helpers.Tests.csproj", "{0E74C637-F5DE-42CF-AF5C-98440676D9F6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2DA84300-4B25-42E0-BB9C-4A32A984C87E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -64,10 +60,6 @@ Global {B39E4558-C557-48E7-AA74-19C5CD809617}.Debug|Any CPU.Build.0 = Debug|Any CPU {B39E4558-C557-48E7-AA74-19C5CD809617}.Release|Any CPU.ActiveCfg = Release|Any CPU {B39E4558-C557-48E7-AA74-19C5CD809617}.Release|Any CPU.Build.0 = Release|Any CPU - {0E74C637-F5DE-42CF-AF5C-98440676D9F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0E74C637-F5DE-42CF-AF5C-98440676D9F6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0E74C637-F5DE-42CF-AF5C-98440676D9F6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0E74C637-F5DE-42CF-AF5C-98440676D9F6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -75,6 +67,5 @@ Global GlobalSection(NestedProjects) = preSolution {132DA282-5894-4570-8916-D8C18ED2CE84} = {9293CA11-360A-4C20-A674-B9E794431BF5} {EA31F915-31F9-4318-B521-1500CDF40DDF} = {9293CA11-360A-4C20-A674-B9E794431BF5} - {0E74C637-F5DE-42CF-AF5C-98440676D9F6} = {2DA84300-4B25-42E0-BB9C-4A32A984C87E} EndGlobalSection EndGlobal