diff --git a/ArchiSteamFarm/ArchiCryptoHelper.cs b/ArchiSteamFarm/ArchiCryptoHelper.cs index 28c32800e..3f9020cc7 100644 --- a/ArchiSteamFarm/ArchiCryptoHelper.cs +++ b/ArchiSteamFarm/ArchiCryptoHelper.cs @@ -31,43 +31,24 @@ using SteamKit2; namespace ArchiSteamFarm { public static class ArchiCryptoHelper { - [ItemNotNull] + private const ushort SteamParentalPbkdf2Iterations = 10000; + private const byte SteamParentalSCryptBlocksCount = 8; + private const ushort SteamParentalSCryptIterations = 8192; + + [NotNull] + private static IEnumerable SteamParentalCharacters => Enumerable.Range('0', 10).Select(character => (byte) character); + + [NotNull] private static IEnumerable SteamParentalCodes { get { - for (char a = '0'; a <= '9'; a++) { - for (char b = '0'; b <= '9'; b++) { - for (char c = '0'; c <= '9'; c++) { - for (char d = '0'; d <= '9'; d++) { - yield return new[] { (byte) a, (byte) b, (byte) c, (byte) d }; - } - } - } - } + HashSet steamParentalCharacters = SteamParentalCharacters.ToHashSet(); + + return from a in steamParentalCharacters from b in steamParentalCharacters from c in steamParentalCharacters from d in steamParentalCharacters select new[] { a, b, c, d }; } } private static byte[] EncryptionKey = Encoding.UTF8.GetBytes(nameof(ArchiSteamFarm)); - internal static string BruteforceSteamParentalCode(byte[] passwordHash, byte[] salt, bool scrypt = true) { - if ((passwordHash == null) || (salt == null)) { - ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt)); - - return null; - } - - byte[] password = scrypt - ? SteamParentalCodes.AsParallel().FirstOrDefault(passwordToTry => passwordHash.SequenceEqual(SCrypt.ComputeDerivedKey(passwordToTry, salt, 8192, 8, 1, null, passwordHash.Length))) - : SteamParentalCodes.AsParallel().FirstOrDefault( - passwordToTry => { - using (HMACSHA1 hmacAlgorithm = new HMACSHA1(passwordToTry)) { - return passwordHash.SequenceEqual(Pbkdf2.ComputeDerivedKey(hmacAlgorithm, salt, 10000, passwordHash.Length)); - } - } - ); - - return password != null ? Encoding.UTF8.GetString(password) : null; - } - internal static string Decrypt(ECryptoMethod cryptoMethod, string encrypted) { if (!Enum.IsDefined(typeof(ECryptoMethod), cryptoMethod) || string.IsNullOrEmpty(encrypted)) { ASF.ArchiLogger.LogNullError(nameof(cryptoMethod) + " || " + nameof(encrypted)); @@ -110,6 +91,39 @@ namespace ArchiSteamFarm { } } + internal static byte[] GenerateSteamParentalHash(byte[] password, byte[] salt, byte hashLength, ESteamParentalAlgorithm steamParentalAlgorithm) { + if ((password == null) || (salt == null) || (hashLength == 0) || !Enum.IsDefined(typeof(ESteamParentalAlgorithm), steamParentalAlgorithm)) { + ASF.ArchiLogger.LogNullError(nameof(password) + " || " + nameof(salt) + " || " + nameof(hashLength) + " || " + nameof(steamParentalAlgorithm)); + + return null; + } + + switch (steamParentalAlgorithm) { + case ESteamParentalAlgorithm.Pbkdf2: + using (HMACSHA1 hmacAlgorithm = new HMACSHA1(password)) { + return Pbkdf2.ComputeDerivedKey(hmacAlgorithm, salt, SteamParentalPbkdf2Iterations, hashLength); + } + case ESteamParentalAlgorithm.SCrypt: + return SCrypt.ComputeDerivedKey(password, salt, SteamParentalSCryptIterations, SteamParentalSCryptBlocksCount, 1, null, hashLength); + default: + ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(steamParentalAlgorithm), steamParentalAlgorithm)); + + return null; + } + } + + internal static string RecoverSteamParentalCode(byte[] passwordHash, byte[] salt, ESteamParentalAlgorithm steamParentalAlgorithm) { + if ((passwordHash == null) || (salt == null) || !Enum.IsDefined(typeof(ESteamParentalAlgorithm), steamParentalAlgorithm)) { + ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt) + " || " + nameof(steamParentalAlgorithm)); + + return null; + } + + byte[] password = SteamParentalCodes.AsParallel().FirstOrDefault(passwordToTry => GenerateSteamParentalHash(passwordToTry, salt, (byte) passwordHash.Length, steamParentalAlgorithm)?.SequenceEqual(passwordHash) == true); + + return password != null ? Encoding.UTF8.GetString(password) : null; + } + internal static void SetEncryptionKey(string key) { if (string.IsNullOrEmpty(key)) { ASF.ArchiLogger.LogNullError(nameof(key)); @@ -227,5 +241,10 @@ namespace ArchiSteamFarm { AES, ProtectedDataForCurrentUser } + + internal enum ESteamParentalAlgorithm : byte { + SCrypt, + Pbkdf2 + } } } diff --git a/ArchiSteamFarm/ArchiHandler.cs b/ArchiSteamFarm/ArchiHandler.cs index 1a384608f..ac18adad8 100644 --- a/ArchiSteamFarm/ArchiHandler.cs +++ b/ArchiSteamFarm/ArchiHandler.cs @@ -25,12 +25,10 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Net; -using System.Security.Cryptography; using System.Threading.Tasks; using ArchiSteamFarm.CMsgs; using ArchiSteamFarm.Localization; using ArchiSteamFarm.NLog; -using CryptSharp.Utility; using JetBrains.Annotations; using SteamKit2; using SteamKit2.Internal; @@ -666,15 +664,15 @@ namespace ArchiSteamFarm { return (false, null); } - bool scrypt; + ArchiCryptoHelper.ESteamParentalAlgorithm steamParentalAlgorithm; switch (body.settings.passwordhashtype) { case 4: - scrypt = false; + steamParentalAlgorithm = ArchiCryptoHelper.ESteamParentalAlgorithm.Pbkdf2; break; case 6: - scrypt = true; + steamParentalAlgorithm = ArchiCryptoHelper.ESteamParentalAlgorithm.SCrypt; break; default: @@ -696,15 +694,7 @@ namespace ArchiSteamFarm { } if (i >= steamParentalCode.Length) { - byte[] passwordHash; - - if (scrypt) { - passwordHash = SCrypt.ComputeDerivedKey(password, body.settings.salt, 8192, 8, 1, null, body.settings.passwordhash.Length); - } else { - using (HMACSHA1 hmacAlgorithm = new HMACSHA1(password)) { - passwordHash = Pbkdf2.ComputeDerivedKey(hmacAlgorithm, body.settings.salt, 10000, body.settings.passwordhash.Length); - } - } + byte[] passwordHash = ArchiCryptoHelper.GenerateSteamParentalHash(password, body.settings.salt, (byte) body.settings.passwordhash.Length, steamParentalAlgorithm); if (passwordHash.SequenceEqual(body.settings.passwordhash)) { return (true, steamParentalCode); @@ -712,9 +702,9 @@ namespace ArchiSteamFarm { } } - ArchiLogger.LogGenericInfo(Strings.PleaseWait); + ArchiLogger.LogGenericInfo(Strings.BotGeneratingSteamParentalCode); - steamParentalCode = ArchiCryptoHelper.BruteforceSteamParentalCode(body.settings.passwordhash, body.settings.salt, scrypt); + steamParentalCode = ArchiCryptoHelper.RecoverSteamParentalCode(body.settings.passwordhash, body.settings.salt, steamParentalAlgorithm); ArchiLogger.LogGenericInfo(Strings.Done); diff --git a/ArchiSteamFarm/Localization/Strings.Designer.cs b/ArchiSteamFarm/Localization/Strings.Designer.cs index 36e038b2f..5dfec0b1b 100644 --- a/ArchiSteamFarm/Localization/Strings.Designer.cs +++ b/ArchiSteamFarm/Localization/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace ArchiSteamFarm.Localization { // przez narzędzie, takie jak ResGen lub Visual Studio. // Aby dodać lub usunąć składową, edytuj plik ResX, a następnie ponownie uruchom narzędzie ResGen // z opcją /str lub ponownie utwórz projekt VS. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Strings { @@ -330,6 +330,15 @@ namespace ArchiSteamFarm.Localization { } } + /// + /// Wyszukuje zlokalizowany ciąg podobny do ciągu Generating Steam parental code, this can take a while, consider putting it in the config instead.... + /// + public static string BotGeneratingSteamParentalCode { + get { + return ResourceManager.GetString("BotGeneratingSteamParentalCode", resourceCulture); + } + } + /// /// Wyszukuje zlokalizowany ciąg podobny do ciągu Successfully handled {0} confirmations!. /// diff --git a/ArchiSteamFarm/Localization/Strings.resx b/ArchiSteamFarm/Localization/Strings.resx index fbb8022df..a6b96bdb7 100644 --- a/ArchiSteamFarm/Localization/Strings.resx +++ b/ArchiSteamFarm/Localization/Strings.resx @@ -731,4 +731,7 @@ StackTrace: Cleaning up old files after update... + + Generating Steam parental code, this can take a while, consider putting it in the config instead... + \ No newline at end of file