diff --git a/ArchiSteamFarm/ArchiCryptoHelper.cs b/ArchiSteamFarm/ArchiCryptoHelper.cs index b0712f198..611b5266a 100644 --- a/ArchiSteamFarm/ArchiCryptoHelper.cs +++ b/ArchiSteamFarm/ArchiCryptoHelper.cs @@ -20,15 +20,27 @@ // limitations under the License. using System; +using System.Linq; using System.Security.Cryptography; using System.Text; using ArchiSteamFarm.Localization; +using CryptSharp.Utility; using SteamKit2; namespace ArchiSteamFarm { public static class ArchiCryptoHelper { private static byte[] EncryptionKey = Encoding.UTF8.GetBytes(nameof(ArchiSteamFarm)); + internal static string BruteforceSteamParentalCode(byte[] passwordHash, byte[] salt, bool derivedKey = true) { + if ((passwordHash == null) || (salt == null)) { + ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt)); + + return null; + } + + return derivedKey ? BruteforceSteamParentalCodeDerived(passwordHash, salt) : BruteforceSteamParentalCodePbkdf2(passwordHash, salt); + } + internal static string Decrypt(ECryptoMethod cryptoMethod, string encrypted) { if (!Enum.IsDefined(typeof(ECryptoMethod), cryptoMethod) || string.IsNullOrEmpty(encrypted)) { ASF.ArchiLogger.LogNullError(nameof(cryptoMethod) + " || " + nameof(encrypted)); @@ -81,6 +93,76 @@ namespace ArchiSteamFarm { EncryptionKey = Encoding.UTF8.GetBytes(key); } + private static string BruteforceSteamParentalCodeDerived(byte[] passwordHash, byte[] salt) { + if ((passwordHash == null) || (salt == null)) { + ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt)); + + return null; + } + + byte[] password = new byte[4]; + + for (char a = '0'; a <= '9'; a++) { + password[0] = (byte) a; + + for (char b = '0'; b <= '9'; b++) { + password[1] = (byte) b; + + for (char c = '0'; c <= '9'; c++) { + password[2] = (byte) c; + + for (char d = '0'; d <= '9'; d++) { + password[3] = (byte) d; + + byte[] passwordHashTry = SCrypt.ComputeDerivedKey(password, salt, 8192, 8, 1, null, passwordHash.Length); + + if (passwordHashTry.SequenceEqual(passwordHash)) { + return Encoding.UTF8.GetString(password); + } + } + } + } + } + + return null; + } + + private static string BruteforceSteamParentalCodePbkdf2(byte[] passwordHash, byte[] salt) { + if ((passwordHash == null) || (salt == null)) { + ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt)); + + return null; + } + + byte[] password = new byte[4]; + + using (KeyedHashAlgorithm hmacAlgorithm = KeyedHashAlgorithm.Create()) { + for (char a = '0'; a <= '9'; a++) { + password[0] = (byte) a; + + for (char b = '0'; b <= '9'; b++) { + password[1] = (byte) b; + + for (char c = '0'; c <= '9'; c++) { + password[2] = (byte) c; + + for (char d = '0'; d <= '9'; d++) { + password[3] = (byte) d; + + byte[] passwordHashTry = Pbkdf2.ComputeDerivedKey(hmacAlgorithm, salt, 10000, passwordHash.Length); + + if (passwordHashTry.SequenceEqual(passwordHash)) { + return Encoding.UTF8.GetString(password); + } + } + } + } + } + } + + return null; + } + private static string DecryptAES(string encrypted) { if (string.IsNullOrEmpty(encrypted)) { ASF.ArchiLogger.LogNullError(nameof(encrypted)); diff --git a/ArchiSteamFarm/ArchiHandler.cs b/ArchiSteamFarm/ArchiHandler.cs index 8aaa53842..8016ec717 100644 --- a/ArchiSteamFarm/ArchiHandler.cs +++ b/ArchiSteamFarm/ArchiHandler.cs @@ -29,6 +29,7 @@ using System.Threading.Tasks; using ArchiSteamFarm.CMsgs; using ArchiSteamFarm.Localization; using ArchiSteamFarm.NLog; +using CryptSharp.Utility; using JetBrains.Annotations; using SteamKit2; using SteamKit2.Internal; @@ -43,6 +44,7 @@ namespace ArchiSteamFarm { private readonly SteamUnifiedMessages.UnifiedService UnifiedClanChatRoomsService; private readonly SteamUnifiedMessages.UnifiedService UnifiedEconService; private readonly SteamUnifiedMessages.UnifiedService UnifiedFriendMessagesService; + private readonly SteamUnifiedMessages.UnifiedService UnifiedParentalService; private readonly SteamUnifiedMessages.UnifiedService UnifiedPlayerService; internal DateTime LastPacketReceived { get; private set; } @@ -57,6 +59,7 @@ namespace ArchiSteamFarm { UnifiedClanChatRoomsService = steamUnifiedMessages.CreateService(); UnifiedEconService = steamUnifiedMessages.CreateService(); UnifiedFriendMessagesService = steamUnifiedMessages.CreateService(); + UnifiedParentalService = steamUnifiedMessages.CreateService(); UnifiedPlayerService = steamUnifiedMessages.CreateService(); } @@ -629,6 +632,86 @@ namespace ArchiSteamFarm { Client.Send(request); } + internal async Task<(bool IsSteamParentalEnabled, string SteamParentalCode)?> ValidateSteamParental(string steamParentalCode = null) { + if (!Client.IsConnected) { + return null; + } + + CParental_GetParentalSettings_Request request = new CParental_GetParentalSettings_Request { steamid = Client.SteamID }; + + SteamUnifiedMessages.ServiceMethodResponse response; + + try { + response = await UnifiedParentalService.SendMessage(x => x.GetParentalSettings(request)); + } catch (Exception e) { + ArchiLogger.LogGenericWarningException(e); + + return null; + } + + if (response == null) { + ArchiLogger.LogNullError(nameof(response)); + + return null; + } + + if (response.Result != EResult.OK) { + return null; + } + + CParental_GetParentalSettings_Response body = response.GetDeserializedResponse(); + + if (!body.settings.is_enabled) { + return (false, null); + } + + bool derivedKey; + + switch (body.settings.passwordhashtype) { + case 4: + derivedKey = false; + + break; + case 6: + derivedKey = true; + + break; + default: + ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(body.settings.passwordhashtype), body.settings.passwordhashtype)); + + return (false, null); + } + + if ((steamParentalCode != null) && (steamParentalCode.Length > 4)) { + byte i = 0; + byte[] password = new byte[steamParentalCode.Length]; + + foreach (char character in steamParentalCode) { + if ((character < '0') || (character > '9')) { + break; + } + + password[i++] = (byte) character; + } + + if (i >= steamParentalCode.Length) { + byte[] passwordHash = derivedKey ? SCrypt.ComputeDerivedKey(password, body.settings.salt, 8192, 8, 1, null, body.settings.passwordhash.Length) : SCrypt.GetEffectivePbkdf2Salt(password, body.settings.salt, 8192, 8, 1, null); + + if (passwordHash.SequenceEqual(body.settings.passwordhash)) { + return (true, steamParentalCode); + } + } + } + + ArchiLogger.LogGenericInfo(Strings.PleaseWait); + + steamParentalCode = ArchiCryptoHelper.BruteforceSteamParentalCode(body.settings.passwordhash, body.settings.salt, derivedKey); + + ArchiLogger.LogGenericInfo(Strings.Done); + + return (true, steamParentalCode); + } + private void HandleItemAnnouncements(IPacketMsg packetMsg) { if (packetMsg == null) { ArchiLogger.LogNullError(nameof(packetMsg)); diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj index 557d50b4b..aa208e057 100644 --- a/ArchiSteamFarm/ArchiSteamFarm.csproj +++ b/ArchiSteamFarm/ArchiSteamFarm.csproj @@ -59,6 +59,7 @@ all + diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index d496856bb..2833931a3 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -2417,16 +2417,24 @@ namespace ArchiSteamFarm { } } - if (!string.IsNullOrEmpty(BotConfig.SteamParentalCode) && (BotConfig.SteamParentalCode.Length != 4)) { - string steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); + (bool isSteamParentalEnabled, string steamParentalCode)? steamParental = await ArchiHandler.ValidateSteamParental(BotConfig.SteamParentalCode).ConfigureAwait(false); - if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != 4)) { - Stop(); + if (steamParental?.isSteamParentalEnabled == true) { + if (!string.IsNullOrEmpty(steamParental.Value.steamParentalCode)) { + if (BotConfig.SteamParentalCode != steamParental.Value.steamParentalCode) { + SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParental.Value.steamParentalCode); + } + } else { + string steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); - break; + if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != 4)) { + Stop(); + + break; + } + + SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode); } - - SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode); } ArchiWebHandler.OnVanityURLChanged(callback.VanityURL);