Slightly improve password generation

This commit is contained in:
JustArchi
2019-07-20 19:55:29 +02:00
parent 68e313c689
commit d4624919e5
4 changed files with 68 additions and 47 deletions

View File

@@ -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<byte> SteamParentalCharacters => Enumerable.Range('0', 10).Select(character => (byte) character);
[NotNull]
private static IEnumerable<byte[]> 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<byte> 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
}
}
}

View File

@@ -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);

View File

@@ -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 {
}
}
/// <summary>
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Generating Steam parental code, this can take a while, consider putting it in the config instead....
/// </summary>
public static string BotGeneratingSteamParentalCode {
get {
return ResourceManager.GetString("BotGeneratingSteamParentalCode", resourceCulture);
}
}
/// <summary>
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Successfully handled {0} confirmations!.
/// </summary>

View File

@@ -731,4 +731,7 @@ StackTrace:
<data name="UpdateCleanup" xml:space="preserve">
<value>Cleaning up old files after update...</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>Generating Steam parental code, this can take a while, consider putting it in the config instead...</value>
</data>
</root>