Add support for encrypted passwords, closes #267

This commit is contained in:
JustArchi
2016-06-28 04:32:48 +02:00
parent a01e718e28
commit 8d9fbce2ed
8 changed files with 222 additions and 4 deletions

View File

@@ -1,4 +1,9 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/EMPTY_BLOCK_STYLE/@EntryValue">TOGETHER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHING_EMPTY_BRACES/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AES/@EntryIndexedValue">AES</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=API/@EntryIndexedValue">API</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=API/@EntryIndexedValue">API</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ASF/@EntryIndexedValue">ASF</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ASF/@EntryIndexedValue">ASF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FA/@EntryIndexedValue">FA</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FA/@EntryIndexedValue">FA</s:String>
@@ -15,4 +20,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WTF/@EntryIndexedValue">WTF</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WTF/@EntryIndexedValue">WTF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XML/@EntryIndexedValue">XML</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XML/@EntryIndexedValue">XML</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="AaBb" /&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="AaBb" /&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -101,6 +101,7 @@
<Compile Include="BotConfig.cs" /> <Compile Include="BotConfig.cs" />
<Compile Include="ConcurrentEnumerator.cs" /> <Compile Include="ConcurrentEnumerator.cs" />
<Compile Include="ConcurrentHashSet.cs" /> <Compile Include="ConcurrentHashSet.cs" />
<Compile Include="CryptoHelper.cs" />
<Compile Include="GlobalDatabase.cs" /> <Compile Include="GlobalDatabase.cs" />
<Compile Include="BotDatabase.cs" /> <Compile Include="BotDatabase.cs" />
<Compile Include="CardsFarmer.cs" /> <Compile Include="CardsFarmer.cs" />

View File

@@ -127,7 +127,7 @@ namespace ArchiSteamFarm {
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString())); string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString()));
// Generate an AES session key // Generate an AES session key
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32); byte[] sessionKey = SteamKit2.CryptoHelper.GenerateRandomBlock(32);
// RSA encrypt it with the public key for the universe we're on // RSA encrypt it with the public key for the universe we're on
byte[] cryptedSessionKey; byte[] cryptedSessionKey;
@@ -140,7 +140,7 @@ namespace ArchiSteamFarm {
Array.Copy(Encoding.ASCII.GetBytes(webAPIUserNonce), loginKey, webAPIUserNonce.Length); Array.Copy(Encoding.ASCII.GetBytes(webAPIUserNonce), loginKey, webAPIUserNonce.Length);
// AES encrypt the loginkey with our session key // AES encrypt the loginkey with our session key
byte[] cryptedLoginKey = CryptoHelper.SymmetricEncrypt(loginKey, sessionKey); byte[] cryptedLoginKey = SteamKit2.CryptoHelper.SymmetricEncrypt(loginKey, sessionKey);
// Do the magic // Do the magic
Logging.LogGenericInfo("Logging in to ISteamUserAuth...", Bot.BotName); Logging.LogGenericInfo("Logging in to ISteamUserAuth...", Bot.BotName);

View File

@@ -385,6 +385,8 @@ namespace ArchiSteamFarm {
return await ResponseLoot(steamID).ConfigureAwait(false); return await ResponseLoot(steamID).ConfigureAwait(false);
case "!LOOTALL": case "!LOOTALL":
return await ResponseLootAll(steamID).ConfigureAwait(false); return await ResponseLootAll(steamID).ConfigureAwait(false);
case "!PASSWORD":
return ResponsePassword(steamID);
case "!PAUSE": case "!PAUSE":
return await ResponsePause(steamID, true).ConfigureAwait(false); return await ResponsePause(steamID, true).ConfigureAwait(false);
case "!REJOINCHAT": case "!REJOINCHAT":
@@ -432,6 +434,8 @@ namespace ArchiSteamFarm {
} }
return await ResponseOwns(steamID, BotName, args[1]).ConfigureAwait(false); return await ResponseOwns(steamID, BotName, args[1]).ConfigureAwait(false);
case "!PASSWORD":
return ResponsePassword(steamID, args[1]);
case "!PAUSE": case "!PAUSE":
return await ResponsePause(steamID, args[1], true).ConfigureAwait(false); return await ResponsePause(steamID, args[1], true).ConfigureAwait(false);
case "!PLAY": case "!PLAY":
@@ -530,6 +534,44 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Successfully finished importing mobile authenticator!", BotName); Logging.LogGenericInfo("Successfully finished importing mobile authenticator!", BotName);
} }
private string ResponsePassword(ulong steamID) {
if (steamID == 0) {
Logging.LogNullError(nameof(steamID), BotName);
return null;
}
if (!IsMaster(steamID)) {
return null;
}
if (string.IsNullOrEmpty(BotConfig.SteamPassword)) {
return "Can't encrypt null password!";
}
return Environment.NewLine +
"Password length: " + BotConfig.SteamPassword.Length + Environment.NewLine +
CryptoHelper.ECryptoMethod.Base64 + " encrypted: " + CryptoHelper.Encrypt(CryptoHelper.ECryptoMethod.Base64, BotConfig.SteamPassword) + Environment.NewLine +
CryptoHelper.ECryptoMethod.AES + " encrypted: " + CryptoHelper.Encrypt(CryptoHelper.ECryptoMethod.AES, BotConfig.SteamPassword);
}
private static string ResponsePassword(ulong steamID, string botName) {
if ((steamID == 0) || string.IsNullOrEmpty(botName)) {
Logging.LogNullError(nameof(steamID) + " || " + nameof(botName));
return null;
}
Bot bot;
if (Bots.TryGetValue(botName, out bot)) {
return bot.ResponsePassword(steamID);
}
if (IsOwner(steamID)) {
return "Couldn't find any bot named " + botName + "!";
}
return null;
}
private async Task<string> ResponsePause(ulong steamID, bool pause) { private async Task<string> ResponsePause(ulong steamID, bool pause) {
if (steamID == 0) { if (steamID == 0) {
Logging.LogNullError(nameof(steamID), BotName); Logging.LogNullError(nameof(steamID), BotName);
@@ -1466,7 +1508,7 @@ namespace ArchiSteamFarm {
if (File.Exists(SentryFile)) { if (File.Exists(SentryFile)) {
try { try {
byte[] sentryFileContent = File.ReadAllBytes(SentryFile); byte[] sentryFileContent = File.ReadAllBytes(SentryFile);
sentryHash = CryptoHelper.SHAHash(sentryFileContent); sentryHash = SteamKit2.CryptoHelper.SHAHash(sentryFileContent);
} catch (Exception e) { } catch (Exception e) {
Logging.LogGenericException(e, BotName); Logging.LogGenericException(e, BotName);
} }

View File

@@ -45,6 +45,10 @@ namespace ArchiSteamFarm {
[JsonProperty] [JsonProperty]
internal string SteamPassword { get; set; } internal string SteamPassword { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
[SuppressMessage("ReSharper", "ConvertToConstant.Local")]
private readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
[JsonProperty] [JsonProperty]
internal string SteamParentalPIN { get; set; } = "0"; internal string SteamParentalPIN { get; set; } = "0";
@@ -125,6 +129,12 @@ namespace ArchiSteamFarm {
return null; return null;
} }
// Support encrypted passwords
if (!string.IsNullOrEmpty(botConfig.SteamPassword) && (botConfig.PasswordFormat != CryptoHelper.ECryptoMethod.PlainText)) {
// In worst case password will result in null, which will have to be corrected by user during runtime
botConfig.SteamPassword = CryptoHelper.Decrypt(botConfig.PasswordFormat, botConfig.SteamPassword);
}
// User might not know what he's doing // User might not know what he's doing
// Ensure that he can't screw core ASF variables // Ensure that he can't screw core ASF variables
if (botConfig.GamesPlayedWhileIdle.Count <= CardsFarmer.MaxGamesPlayedConcurrently) { if (botConfig.GamesPlayedWhileIdle.Count <= CardsFarmer.MaxGamesPlayedConcurrently) {

View File

@@ -0,0 +1,147 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System;
using System.Security.Cryptography;
using System.Text;
namespace ArchiSteamFarm {
internal static class CryptoHelper {
internal enum ECryptoMethod : byte {
PlainText,
Base64,
AES
}
private static readonly byte[] EncryptionKey = Encoding.UTF8.GetBytes("ArchiSteamFarm");
internal static string Encrypt(ECryptoMethod cryptoMethod, string decrypted) {
if (string.IsNullOrEmpty(decrypted)) {
Logging.LogNullError(nameof(decrypted));
return null;
}
switch (cryptoMethod) {
case ECryptoMethod.PlainText:
return decrypted;
case ECryptoMethod.Base64:
return EncryptBase64(decrypted);
case ECryptoMethod.AES:
return EncryptAES(decrypted);
default:
return null;
}
}
internal static string Decrypt(ECryptoMethod cryptoMethod, string encrypted) {
if (string.IsNullOrEmpty(encrypted)) {
Logging.LogNullError(nameof(encrypted));
return null;
}
switch (cryptoMethod) {
case ECryptoMethod.PlainText:
return encrypted;
case ECryptoMethod.Base64:
return DecryptBase64(encrypted);
case ECryptoMethod.AES:
return DecryptAES(encrypted);
default:
return null;
}
}
private static string EncryptBase64(string decrypted) {
if (string.IsNullOrEmpty(decrypted)) {
Logging.LogNullError(nameof(decrypted));
return null;
}
try {
byte[] data = Encoding.UTF8.GetBytes(decrypted);
return Convert.ToBase64String(data);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
}
private static string DecryptBase64(string encrypted) {
if (string.IsNullOrEmpty(encrypted)) {
Logging.LogNullError(nameof(encrypted));
return null;
}
try {
byte[] data = Convert.FromBase64String(encrypted);
return Encoding.UTF8.GetString(data);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
}
private static string EncryptAES(string decrypted) {
if (string.IsNullOrEmpty(decrypted)) {
Logging.LogNullError(nameof(decrypted));
return null;
}
try {
byte[] key;
using (SHA256Managed sha256 = new SHA256Managed()) {
key = sha256.ComputeHash(EncryptionKey);
}
byte[] data = Encoding.UTF8.GetBytes(decrypted);
byte[] encrypted = SteamKit2.CryptoHelper.SymmetricEncrypt(data, key);
return Convert.ToBase64String(encrypted);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
}
private static string DecryptAES(string encrypted) {
if (string.IsNullOrEmpty(encrypted)) {
Logging.LogNullError(nameof(encrypted));
return null;
}
try {
byte[] key;
using (SHA256Managed sha256 = new SHA256Managed()) {
key = sha256.ComputeHash(EncryptionKey);
}
byte[] data = Convert.FromBase64String(encrypted);
byte[] decrypted = SteamKit2.CryptoHelper.SymmetricDecrypt(data, key);
return Encoding.UTF8.GetString(decrypted);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
}
}
}

View File

@@ -3,6 +3,7 @@
"StartOnLaunch": true, "StartOnLaunch": true,
"SteamLogin": null, "SteamLogin": null,
"SteamPassword": null, "SteamPassword": null,
"PasswordFormat": 1,
"SteamParentalPIN": "0", "SteamParentalPIN": "0",
"SteamApiKey": null, "SteamApiKey": null,
"SteamMasterID": 0, "SteamMasterID": 0,

View File

@@ -35,6 +35,12 @@ namespace ConfigGenerator {
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
internal sealed class BotConfig : ASFConfig { internal sealed class BotConfig : ASFConfig {
internal enum ECryptoMethod : byte {
PlainText,
Base64,
AES
}
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public bool Enabled { get; set; } = false; public bool Enabled { get; set; } = false;
@@ -48,6 +54,9 @@ namespace ConfigGenerator {
[PasswordPropertyText(true)] [PasswordPropertyText(true)]
public string SteamPassword { get; set; } = null; public string SteamPassword { get; set; } = null;
[JsonProperty(Required = Required.DisallowNull)]
public ECryptoMethod PasswordFormat { get; set; } = ECryptoMethod.PlainText;
[JsonProperty] [JsonProperty]
public string SteamParentalPIN { get; set; } = "0"; public string SteamParentalPIN { get; set; } = "0";