From c10b353fc972190a1e972239acda139b76d1d5a4 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 13:50:43 -0400 Subject: [PATCH 01/19] Changed how mafiles are read --- Manifest.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index 734c0b5..c4e80fe 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -224,14 +224,27 @@ public class Manifest List accounts = new List(); foreach (var entry in this.Entries) { - string fileText = File.ReadAllText(Path.Combine(Program.SteamGuardPath, entry.Filename)); + string fileText = ""; + Stream stream = null; + FileStream fileStream = File.OpenRead(Path.Combine(Program.SteamGuardPath, entry.Filename)); + if (this.Encrypted) { - throw new NotSupportedException("Encrypted maFiles are not supported at this time."); //string decryptedText = FileEncryptor.DecryptData(passKey, entry.Salt, entry.IV, fileText); - //if (decryptedText == null) return new SteamGuardAccount[0]; - //fileText = decryptedText; } + else + { + stream = fileStream; + } + + byte[] b = new byte[1024]; + StringBuilder sb = new StringBuilder(); + while (stream.Read(b,0,b.Length) > 0) + { + sb.Append(Encoding.UTF8.GetString(b)); + } + stream.Close(); + fileText = sb.ToString(); var account = JsonConvert.DeserializeObject(fileText); if (account == null) continue; From 270749446af4a0d83a192a62091d323f6a2c7de0 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 14:13:36 -0400 Subject: [PATCH 02/19] copied some crypto functions from SDA --- Manifest.cs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Manifest.cs b/Manifest.cs index c4e80fe..c1c8459 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -6,9 +6,15 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Security.Cryptography; public class Manifest { + private const int PBKDF2_ITERATIONS = 50000; //Set to 50k to make program not unbearably slow. May increase in future. + private const int SALT_LENGTH = 8; + private const int KEY_SIZE_BYTES = 32; + private const int IV_LENGTH = 16; + [JsonProperty("encrypted")] public bool Encrypted { get; set; } @@ -433,4 +439,59 @@ public class Manifest [JsonProperty("steamid")] public ulong SteamID { get; set; } } + + /* + Crypto Functions + */ + + /// + /// Returns an 8-byte cryptographically random salt in base64 encoding + /// + /// + public static string GetRandomSalt() + { + byte[] salt = new byte[SALT_LENGTH]; + using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(salt); + } + return Convert.ToBase64String(salt); + } + + /// + /// Returns a 16-byte cryptographically random initialization vector (IV) in base64 encoding + /// + /// + public static string GetInitializationVector() + { + byte[] IV = new byte[IV_LENGTH]; + using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(IV); + } + return Convert.ToBase64String(IV); + } + + + /// + /// Generates an encryption key derived using a password, a random salt, and specified number of rounds of PBKDF2 + /// + /// + /// + /// + private static byte[] GetEncryptionKey(string password, string salt) + { + if (string.IsNullOrEmpty(password)) + { + throw new ArgumentException("Password is empty"); + } + if (string.IsNullOrEmpty(salt)) + { + throw new ArgumentException("Salt is empty"); + } + using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, Convert.FromBase64String(salt), PBKDF2_ITERATIONS)) + { + return pbkdf2.GetBytes(KEY_SIZE_BYTES); + } + } } From 00474371a85138535e23a4167126f2ffe58c7b34 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 14:14:26 -0400 Subject: [PATCH 03/19] added a reference to System.Security for crypto functions --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index c281411..809621b 100644 --- a/makefile +++ b/makefile @@ -3,7 +3,7 @@ all: Program.cs nuget restore SteamAuth/SteamAuth/SteamAuth.sln mcs -target:library -out:build/SteamAuth.dll -r:SteamAuth/SteamAuth/packages/Newtonsoft.Json.7.0.1/lib/net45/Newtonsoft.Json.dll SteamAuth/SteamAuth/APIEndpoints.cs SteamAuth/SteamAuth/AuthenticatorLinker.cs SteamAuth/SteamAuth/Confirmation.cs SteamAuth/SteamAuth/SessionData.cs SteamAuth/SteamAuth/SteamGuardAccount.cs SteamAuth/SteamAuth/SteamWeb.cs SteamAuth/SteamAuth/TimeAligner.cs SteamAuth/SteamAuth/UserLogin.cs SteamAuth/SteamAuth/Util.cs SteamAuth/SteamAuth/Properties/AssemblyInfo.cs cp SteamAuth/SteamAuth/packages/Newtonsoft.Json.7.0.1/lib/net45/Newtonsoft.Json.dll build/ - mcs -out:build/steamguard -r:build/SteamAuth.dll -r:build/Newtonsoft.Json.dll Program.cs Manifest.cs + mcs -out:build/steamguard -r:build/SteamAuth.dll -r:build/Newtonsoft.Json.dll -r:/usr/lib/mono/4.5/System.Security.dll Program.cs Manifest.cs run: build/steamguard -v From 374fe6c7930d565307e4d286fde525e81e35d3ff Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 15:51:37 -0400 Subject: [PATCH 04/19] added encrypted maFile reading, writing added passkey prompt --- Manifest.cs | 55 ++++++++++++++++++++++++++++++++++++++++------------- Program.cs | 10 +++++++++- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index c1c8459..8651ac8 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -83,11 +83,6 @@ public class Manifest _manifest.Save(); } - if (_manifest.Encrypted) - { - throw new NotSupportedException("Encrypted maFiles are not supported at this time."); - } - _manifest.RecomputeExistingEntries(); return _manifest; @@ -233,10 +228,22 @@ public class Manifest string fileText = ""; Stream stream = null; FileStream fileStream = File.OpenRead(Path.Combine(Program.SteamGuardPath, entry.Filename)); + RijndaelManaged aes256; if (this.Encrypted) { - //string decryptedText = FileEncryptor.DecryptData(passKey, entry.Salt, entry.IV, fileText); + byte[] key = GetEncryptionKey(passKey, entry.Salt); + + aes256 = new RijndaelManaged + { + IV = Convert.FromBase64String(entry.IV), + Key = key, + Padding = PaddingMode.PKCS7, + Mode = CipherMode.CBC + }; + + ICryptoTransform decryptor = aes256.CreateDecryptor(aes256.Key, aes256.IV); + stream = new CryptoStream(fileStream, decryptor, CryptoStreamMode.Read); } else { @@ -314,12 +321,6 @@ public class Manifest string iV = null; string jsonAccount = JsonConvert.SerializeObject(account); - if (encrypt) - { - throw new NotSupportedException("Encrypted maFiles are not supported at this time."); - } - - string filename = account.Session.SteamID.ToString() + ".maFile"; ManifestEntry newEntry = new ManifestEntry() @@ -357,7 +358,35 @@ public class Manifest try { - File.WriteAllText(Program.SteamGuardPath + filename, jsonAccount); + Stream stream = null; + FileStream fileStream = File.OpenWrite(Path.Combine(Program.SteamGuardPath, newEntry.Filename)); + RijndaelManaged aes256; + + if (this.Encrypted) + { + byte[] key = GetEncryptionKey(passKey, newEntry.Salt); + + aes256 = new RijndaelManaged + { + IV = Convert.FromBase64String(newEntry.IV), + Key = key, + Padding = PaddingMode.PKCS7, + Mode = CipherMode.CBC + }; + + ICryptoTransform decryptor = aes256.CreateDecryptor(aes256.Key, aes256.IV); + stream = new CryptoStream(fileStream, decryptor, CryptoStreamMode.Write); + } + else + { + stream = fileStream; + } + + using (StreamWriter writer = new StreamWriter(stream)) + { + writer.Write(jsonAccount); + } + stream.Close(); return true; } catch (Exception) diff --git a/Program.cs b/Program.cs index 05f5159..e688719 100644 --- a/Program.cs +++ b/Program.cs @@ -78,7 +78,15 @@ public static class Program if (Verbose) Console.WriteLine("Opening manifest..."); Manifest = Manifest.GetManifest(true); if (Verbose) Console.WriteLine("Reading accounts from manifest..."); - SteamGuardAccounts = Manifest.GetAllAccounts(); + if (Manifest.Encrypted) + { + string passkey = Manifest.PromptForPassKey(); + SteamGuardAccounts = Manifest.GetAllAccounts(passkey); + } + else + { + SteamGuardAccounts = Manifest.GetAllAccounts(); + } if (SteamGuardAccounts.Length == 0) { Console.WriteLine("error: No accounts read."); From 6fab87b485546d564530acd3964803c52172ee91 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 17:11:13 -0400 Subject: [PATCH 05/19] fixed CryptographicException: Length of the data to decrypt is invalid. when attempting to decrypt --- Manifest.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index 8651ac8..4a48472 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -227,11 +227,11 @@ public class Manifest { string fileText = ""; Stream stream = null; - FileStream fileStream = File.OpenRead(Path.Combine(Program.SteamGuardPath, entry.Filename)); RijndaelManaged aes256; if (this.Encrypted) { + MemoryStream ms = new MemoryStream(Convert.FromBase64String(File.ReadAllText(Path.Combine(Program.SteamGuardPath, entry.Filename)))); byte[] key = GetEncryptionKey(passKey, entry.Salt); aes256 = new RijndaelManaged @@ -243,21 +243,20 @@ public class Manifest }; ICryptoTransform decryptor = aes256.CreateDecryptor(aes256.Key, aes256.IV); - stream = new CryptoStream(fileStream, decryptor, CryptoStreamMode.Read); + stream = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); } else { + FileStream fileStream = File.OpenRead(Path.Combine(Program.SteamGuardPath, entry.Filename)); stream = fileStream; } - byte[] b = new byte[1024]; - StringBuilder sb = new StringBuilder(); - while (stream.Read(b,0,b.Length) > 0) + if (Program.Verbose) Console.WriteLine("Decrypting..."); + using (StreamReader reader = new StreamReader(stream)) { - sb.Append(Encoding.UTF8.GetString(b)); + fileText = reader.ReadToEnd(); } stream.Close(); - fileText = sb.ToString(); var account = JsonConvert.DeserializeObject(fileText); if (account == null) continue; From d33ddc0d4a060d16b20619166337d270144535e0 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 18:42:44 -0400 Subject: [PATCH 06/19] added Manifest.GetAccount method added convertion to base64 when saving accounts --- Manifest.cs | 95 +++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index 4a48472..a9730e2 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -205,15 +205,8 @@ public class Manifest } } while (newPassKey != confirmPassKey); - if (!this.ChangeEncryptionKey(null, newPassKey)) - { - Console.WriteLine("Unable to set passkey."); - return null; - } - else - { - Console.WriteLine("Passkey successfully set."); - } + Console.WriteLine("Unable to set passkey."); + return null; return newPassKey; } @@ -225,40 +218,7 @@ public class Manifest List accounts = new List(); foreach (var entry in this.Entries) { - string fileText = ""; - Stream stream = null; - RijndaelManaged aes256; - - if (this.Encrypted) - { - MemoryStream ms = new MemoryStream(Convert.FromBase64String(File.ReadAllText(Path.Combine(Program.SteamGuardPath, entry.Filename)))); - byte[] key = GetEncryptionKey(passKey, entry.Salt); - - aes256 = new RijndaelManaged - { - IV = Convert.FromBase64String(entry.IV), - Key = key, - Padding = PaddingMode.PKCS7, - Mode = CipherMode.CBC - }; - - ICryptoTransform decryptor = aes256.CreateDecryptor(aes256.Key, aes256.IV); - stream = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); - } - else - { - FileStream fileStream = File.OpenRead(Path.Combine(Program.SteamGuardPath, entry.Filename)); - stream = fileStream; - } - - if (Program.Verbose) Console.WriteLine("Decrypting..."); - using (StreamReader reader = new StreamReader(stream)) - { - fileText = reader.ReadToEnd(); - } - stream.Close(); - - var account = JsonConvert.DeserializeObject(fileText); + var account = GetAccount(entry, passKey); if (account == null) continue; accounts.Add(account); @@ -269,9 +229,42 @@ public class Manifest return accounts.ToArray(); } - public bool ChangeEncryptionKey(string oldKey, string newKey) + public SteamGuardAccount GetAccount(ManifestEntry entry, string passKey = null) { - throw new NotSupportedException("Encrypted maFiles are not supported at this time."); + string fileText = ""; + Stream stream = null; + RijndaelManaged aes256; + + if (this.Encrypted) + { + MemoryStream ms = new MemoryStream(Convert.FromBase64String(File.ReadAllText(Path.Combine(Program.SteamGuardPath, entry.Filename)))); + byte[] key = GetEncryptionKey(passKey, entry.Salt); + + aes256 = new RijndaelManaged + { + IV = Convert.FromBase64String(entry.IV), + Key = key, + Padding = PaddingMode.PKCS7, + Mode = CipherMode.CBC + }; + + ICryptoTransform decryptor = aes256.CreateDecryptor(aes256.Key, aes256.IV); + stream = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); + } + else + { + FileStream fileStream = File.OpenRead(Path.Combine(Program.SteamGuardPath, entry.Filename)); + stream = fileStream; + } + + if (Program.Verbose) Console.WriteLine("Decrypting..."); + using (StreamReader reader = new StreamReader(stream)) + { + fileText = reader.ReadToEnd(); + } + stream.Close(); + + return JsonConvert.DeserializeObject(fileText); } public bool VerifyPasskey(string passkey) @@ -359,10 +352,12 @@ public class Manifest { Stream stream = null; FileStream fileStream = File.OpenWrite(Path.Combine(Program.SteamGuardPath, newEntry.Filename)); + MemoryStream ms = null; RijndaelManaged aes256; - if (this.Encrypted) + if (Encrypted) { + ms = new MemoryStream(); byte[] key = GetEncryptionKey(passKey, newEntry.Salt); aes256 = new RijndaelManaged @@ -374,7 +369,7 @@ public class Manifest }; ICryptoTransform decryptor = aes256.CreateDecryptor(aes256.Key, aes256.IV); - stream = new CryptoStream(fileStream, decryptor, CryptoStreamMode.Write); + stream = new CryptoStream(ms, decryptor, CryptoStreamMode.Write); } else { @@ -385,6 +380,12 @@ public class Manifest { writer.Write(jsonAccount); } + + if (Encrypted) + { + File.WriteAllText(Convert.ToBase64String(ms.ToArray()), Path.Combine(Program.SteamGuardPath, newEntry.Filename)); + } + stream.Close(); return true; } From 9b58adb6a235e8efee4150150d27cf655bea0d32 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 18:52:02 -0400 Subject: [PATCH 07/19] Added some TODO --- Manifest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Manifest.cs b/Manifest.cs index a9730e2..a95d009 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -156,6 +156,8 @@ public class Manifest public class IncorrectPassKeyException : Exception { } public class ManifestNotEncryptedException : Exception { } + // TODO: move PromptForPassKey to Program.cs + // TODO: make PromptForPassKey more secure public string PromptForPassKey() { if (!this.Encrypted) @@ -180,6 +182,7 @@ public class Manifest return passKey; } + // TODO: move PromptSetupPassKey to Program.cs public string PromptSetupPassKey(string initialPrompt = "Enter passkey, or hit cancel to remain unencrypted.") { Console.Write("Would you like to use encryption? [Y/n] "); From 725562ffcd641b4a4d0b0b9f2a989f0f212aa10e Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 18:54:14 -0400 Subject: [PATCH 08/19] Added a Path.Combine in GetManifest --- Manifest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Manifest.cs b/Manifest.cs index a95d009..df44598 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -55,7 +55,7 @@ public class Manifest } // Find config dir and manifest file - string maFile = Program.SteamGuardPath + "/manifest.json"; + string maFile = Path.Combine(Program.SteamGuardPath, "manifest.json"); // If there's no config dir, create it if (!Directory.Exists(Program.SteamGuardPath)) From a4f164a128dd6bfd38c459d9b7f12cb422c90fdf Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 19:16:43 -0400 Subject: [PATCH 09/19] added action based system where the program will perform different actions based on cli arguments. --- Program.cs | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/Program.cs b/Program.cs index e688719..a61d410 100644 --- a/Program.cs +++ b/Program.cs @@ -22,6 +22,8 @@ public static class Program [STAThread] public static void Main(string[] args) { + string action = "generate-code"; + string user = ""; // Parse cli arguments @@ -36,6 +38,24 @@ public static class Program return; } Verbose = args.Contains("-v") || args.Contains("--verbose"); + // Actions + if (args.Contains("--generate-code")) + { + action = "generate-code"; + } + else if (args.Contains("--encrypt")) + { + action = "encrypt"; + } + else if (args.Contains("--decrypt")) + { + action = "decrypt"; + } + else if (args.Contains("--setup")) + { + action = "setup"; + } + // Misc if (args.Contains("--user") || args.Contains("-u")) { int u = Array.IndexOf(args, "--user"); @@ -72,7 +92,26 @@ public static class Program } if (Verbose) Console.WriteLine("maFiles path: {0}", SteamGuardPath); - // Generate the code + // Perform desired action + switch (action) + { + case "generate-code": + GenerateCode(user); + break; + case "encrypt": + break; + case "decrypt": + break; + case "setup": + break; + default: + Console.WriteLine("error: Unknown action: {0}", action); + return; + } + } + + static void GenerateCode(string user = "") + { if (Verbose) Console.WriteLine("Aligning time..."); TimeAligner.AlignTime(); if (Verbose) Console.WriteLine("Opening manifest..."); From 34a78da41642eb9b69bfd01f2afd8ae93aa79644 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 21:12:48 -0400 Subject: [PATCH 10/19] implemented --encrypt --- Manifest.cs | 51 ++++++++++++++++++++++----------------------------- Program.cs | 29 ++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index df44598..fd61854 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -41,11 +41,6 @@ public class Manifest private static Manifest _manifest { get; set; } - public static string GetExecutableDir() - { - return Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); - } - public static Manifest GetManifest(bool forceLoad = false) { // Check if already staticly loaded @@ -111,7 +106,6 @@ public class Manifest // Take a pre-manifest version and generate a manifest for it. if (scanDir) { - if (Directory.Exists(Program.SteamGuardPath)) { DirectoryInfo dir = new DirectoryInfo(Program.SteamGuardPath); @@ -132,15 +126,16 @@ public class Manifest }; newManifest.Entries.Add(newEntry); } - catch (Exception) + catch (Exception ex) { + if (Program.Verbose) Console.WriteLine("warn: {0}", ex.Message); } } if (newManifest.Entries.Count > 0) { newManifest.Save(); - newManifest.PromptSetupPassKey("This version of SDA has encryption. Please enter a passkey below, or hit cancel to remain unencrypted"); + newManifest.PromptSetupPassKey(true); } } } @@ -183,33 +178,33 @@ public class Manifest } // TODO: move PromptSetupPassKey to Program.cs - public string PromptSetupPassKey(string initialPrompt = "Enter passkey, or hit cancel to remain unencrypted.") + public string PromptSetupPassKey(bool inAccountSetupProcess = false) { - Console.Write("Would you like to use encryption? [Y/n] "); - string doEncryptAnswer = Console.ReadLine(); - if (doEncryptAnswer == "n" || doEncryptAnswer == "N") + if (inAccountSetupProcess) { - Console.WriteLine("WARNING: You chose to not encrypt your files. Doing so imposes a security risk for yourself. If an attacker were to gain access to your computer, they could completely lock you out of your account and steal all your items."); - return null; + Console.Write("Would you like to use encryption? [Y/n] "); + string doEncryptAnswer = Console.ReadLine(); + if (doEncryptAnswer == "n" || doEncryptAnswer == "N") + { + Console.WriteLine("WARNING: You chose to not encrypt your files. Doing so imposes a security risk for yourself. If an attacker were to gain access to your computer, they could completely lock you out of your account and steal all your items."); + return null; + } } string newPassKey = ""; string confirmPassKey = ""; do { - Console.Write("Enter passkey: "); + Console.Write("Enter" + (inAccountSetupProcess ? " " : " new ") + "passkey: "); newPassKey = Console.ReadLine(); - Console.Write("Confirm passkey: "); + Console.Write("Confirm" + (inAccountSetupProcess ? " " : " new ") + "passkey: "); confirmPassKey = Console.ReadLine(); if (newPassKey != confirmPassKey) { Console.WriteLine("Passkeys do not match."); } - } while (newPassKey != confirmPassKey); - - Console.WriteLine("Unable to set passkey."); - return null; + } while (newPassKey != confirmPassKey || newPassKey == ""); return newPassKey; } @@ -307,13 +302,11 @@ public class Manifest return false; } - public bool SaveAccount(SteamGuardAccount account, bool encrypt, string passKey = null) + public bool SaveAccount(SteamGuardAccount account, bool encrypt, string passKey = null, string salt = null, string iV = null) { if (encrypt && String.IsNullOrEmpty(passKey)) return false; if (!encrypt && this.Encrypted) return false; - string salt = null; - string iV = null; string jsonAccount = JsonConvert.SerializeObject(account); string filename = account.Session.SteamID.ToString() + ".maFile"; @@ -354,7 +347,6 @@ public class Manifest try { Stream stream = null; - FileStream fileStream = File.OpenWrite(Path.Combine(Program.SteamGuardPath, newEntry.Filename)); MemoryStream ms = null; RijndaelManaged aes256; @@ -371,12 +363,12 @@ public class Manifest Mode = CipherMode.CBC }; - ICryptoTransform decryptor = aes256.CreateDecryptor(aes256.Key, aes256.IV); - stream = new CryptoStream(ms, decryptor, CryptoStreamMode.Write); + ICryptoTransform encryptor = aes256.CreateEncryptor(aes256.Key, aes256.IV); + stream = new CryptoStream(ms, encryptor, CryptoStreamMode.Write); } else { - stream = fileStream; + stream = File.OpenWrite(Path.Combine(Program.SteamGuardPath, newEntry.Filename)); } using (StreamWriter writer = new StreamWriter(stream)) @@ -386,14 +378,15 @@ public class Manifest if (Encrypted) { - File.WriteAllText(Convert.ToBase64String(ms.ToArray()), Path.Combine(Program.SteamGuardPath, newEntry.Filename)); + File.WriteAllText(Path.Combine(Program.SteamGuardPath, newEntry.Filename), Convert.ToBase64String(ms.ToArray())); } stream.Close(); return true; } - catch (Exception) + catch (Exception ex) { + if (Program.Verbose) Console.WriteLine("error: {0}", ex.ToString()); return false; } } diff --git a/Program.cs b/Program.cs index a61d410..0ece78f 100644 --- a/Program.cs +++ b/Program.cs @@ -92,13 +92,15 @@ public static class Program } if (Verbose) Console.WriteLine("maFiles path: {0}", SteamGuardPath); + if (Verbose) Console.WriteLine("Action: {0}", action); // Perform desired action switch (action) { case "generate-code": GenerateCode(user); break; - case "encrypt": + case "encrypt": // Can also be used to change passkey + Encrypt(); break; case "decrypt": break; @@ -157,4 +159,29 @@ public static class Program else Console.WriteLine("error: No Steam accounts found in {0}", SteamGuardAccounts); } + + static void Encrypt() + { + if (Verbose) Console.WriteLine("Opening manifest..."); + Manifest = Manifest.GetManifest(true); + if (Verbose) Console.WriteLine("Reading accounts from manifest..."); + if (Manifest.Encrypted) + { + string passkey = Manifest.PromptForPassKey(); + SteamGuardAccounts = Manifest.GetAllAccounts(passkey); + } + else + { + SteamGuardAccounts = Manifest.GetAllAccounts(); + } + + string newPassKey = Manifest.PromptSetupPassKey(); + + for (int i = 0; i < SteamGuardAccounts.Length; i++) + { + var account = SteamGuardAccounts[i]; + bool success = Manifest.SaveAccount(account, true, newPassKey, Manifest.GetRandomSalt(), Manifest.GetInitializationVector()); + if (Verbose) Console.WriteLine("Encrypted {0}: {1}", account.AccountName, success); + } + } } From 3f3bf6e9d896a80250423bdb5e24f815665582ed Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 21:20:55 -0400 Subject: [PATCH 11/19] Updated help text --- Program.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Program.cs b/Program.cs index 0ece78f..5cd21bf 100644 --- a/Program.cs +++ b/Program.cs @@ -31,10 +31,13 @@ public static class Program { Console.WriteLine("steamguard-cli - v0.0"); Console.WriteLine(); - Console.WriteLine("--help, -h Display this help message."); - Console.WriteLine("--verbose, -v Display some extra information when the program is running."); - Console.WriteLine("--user, -u Specify an account for which to generate a Steam Gaurd code."); - Console.WriteLine(" Otherwise, the first account will be selected."); + Console.WriteLine("--help, -h Display this help message."); + Console.WriteLine("--verbose, -v Display some extra information when the program is running."); + Console.WriteLine("--user, -u Specify an account for which to generate a Steam Gaurd code."); + Console.WriteLine(" Otherwise, the first account will be selected."); + Console.WriteLine("--generate-code Generate a Steam Guard code and exit. (default)"); + Console.WriteLine("--encrypt Encrypt your maFiles or change your encryption passkey."); + Console.WriteLine("--decrypt Remove encryption from your maFiles."); return; } Verbose = args.Contains("-v") || args.Contains("--verbose"); @@ -105,6 +108,7 @@ public static class Program case "decrypt": break; case "setup": + throw new NotSupportedException(); break; default: Console.WriteLine("error: Unknown action: {0}", action); From 8ed592f08fb72ee365db950876280e8969b0c00e Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Mon, 22 Aug 2016 22:16:47 -0400 Subject: [PATCH 12/19] implemented --decrypt --- Program.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Program.cs b/Program.cs index 5cd21bf..74e621b 100644 --- a/Program.cs +++ b/Program.cs @@ -106,6 +106,7 @@ public static class Program Encrypt(); break; case "decrypt": + Decrypt(); break; case "setup": throw new NotSupportedException(); @@ -188,4 +189,28 @@ public static class Program if (Verbose) Console.WriteLine("Encrypted {0}: {1}", account.AccountName, success); } } + + static void Decrypt() + { + if (Verbose) Console.WriteLine("Opening manifest..."); + Manifest = Manifest.GetManifest(true); + if (Verbose) Console.WriteLine("Reading accounts from manifest..."); + if (Manifest.Encrypted) + { + string passkey = Manifest.PromptForPassKey(); + SteamGuardAccounts = Manifest.GetAllAccounts(passkey); + } + else + { + if (Verbose) Console.WriteLine("Decryption not required."); + return; + } + + for (int i = 0; i < SteamGuardAccounts.Length; i++) + { + var account = SteamGuardAccounts[i]; + bool success = Manifest.SaveAccount(account, false); + if (Verbose) Console.WriteLine("Decrypted {0}: {1}", account.AccountName, success); + } + } } From f31e38d0aaff8917d5b4ec8c88efecac61660b7b Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 23 Aug 2016 10:38:53 -0400 Subject: [PATCH 13/19] I dont know what i did, but it doesn't break building. --- Manifest.cs | 2 +- Program.cs | 2 +- makefile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index fd61854..a4bf1e1 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using SteamAuth; using System; using System.Collections.Generic; diff --git a/Program.cs b/Program.cs index 74e621b..f74ece3 100644 --- a/Program.cs +++ b/Program.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using SteamAuth; using System; using System.Collections.Generic; diff --git a/makefile b/makefile index 809621b..734f938 100644 --- a/makefile +++ b/makefile @@ -1,4 +1,4 @@ -all: Program.cs +all: Program.cs mkdir -p build/ nuget restore SteamAuth/SteamAuth/SteamAuth.sln mcs -target:library -out:build/SteamAuth.dll -r:SteamAuth/SteamAuth/packages/Newtonsoft.Json.7.0.1/lib/net45/Newtonsoft.Json.dll SteamAuth/SteamAuth/APIEndpoints.cs SteamAuth/SteamAuth/AuthenticatorLinker.cs SteamAuth/SteamAuth/Confirmation.cs SteamAuth/SteamAuth/SessionData.cs SteamAuth/SteamAuth/SteamGuardAccount.cs SteamAuth/SteamAuth/SteamWeb.cs SteamAuth/SteamAuth/TimeAligner.cs SteamAuth/SteamAuth/UserLogin.cs SteamAuth/SteamAuth/Util.cs SteamAuth/SteamAuth/Properties/AssemblyInfo.cs From c0c14e1fc353b979fa29e72618469edb4486e0f4 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 23 Aug 2016 13:29:49 -0400 Subject: [PATCH 14/19] added some verbose logging statements --- Manifest.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index a4bf1e1..edd1487 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -310,6 +310,7 @@ public class Manifest string jsonAccount = JsonConvert.SerializeObject(account); string filename = account.Session.SteamID.ToString() + ".maFile"; + if (Program.Verbose) Console.WriteLine($"Saving account {account.AccountName} to {filename}..."); ManifestEntry newEntry = new ManifestEntry() { @@ -393,16 +394,17 @@ public class Manifest public bool Save() { - - string filename = Program.SteamGuardPath + "manifest.json"; + string filename = Path.Combine(Program.SteamGuardPath, "manifest.json"); if (!Directory.Exists(Program.SteamGuardPath)) { try { + if (Program.Verbose) Console.WriteLine("Creating {0}", Program.SteamGuardPath); Directory.CreateDirectory(Program.SteamGuardPath); } - catch (Exception) + catch (Exception ex) { + if (Program.Verbose) Console.WriteLine($"error: {ex.Message}"); return false; } } @@ -413,8 +415,9 @@ public class Manifest File.WriteAllText(filename, contents); return true; } - catch (Exception) + catch (Exception ex) { + if (Program.Verbose) Console.WriteLine($"error: {ex.Message}"); return false; } } From 1b6a70259368a7d3b58ab900e3dad8ca80ea4056 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 23 Aug 2016 13:49:52 -0400 Subject: [PATCH 15/19] Fixed encryption changes not being written --- Manifest.cs | 10 +++++----- Program.cs | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index edd1487..255b4f6 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -304,8 +304,8 @@ public class Manifest public bool SaveAccount(SteamGuardAccount account, bool encrypt, string passKey = null, string salt = null, string iV = null) { - if (encrypt && String.IsNullOrEmpty(passKey)) return false; - if (!encrypt && this.Encrypted) return false; + if (encrypt && (String.IsNullOrEmpty(passKey) || String.IsNullOrEmpty(salt) || String.IsNullOrEmpty(iV))) return false; + //if (!encrypt && this.Encrypted) return false; string jsonAccount = JsonConvert.SerializeObject(account); @@ -337,7 +337,7 @@ public class Manifest } bool wasEncrypted = this.Encrypted; - this.Encrypted = encrypt || this.Encrypted; + this.Encrypted = encrypt;// || this.Encrypted; if (!this.Save()) { @@ -351,7 +351,7 @@ public class Manifest MemoryStream ms = null; RijndaelManaged aes256; - if (Encrypted) + if (encrypt) { ms = new MemoryStream(); byte[] key = GetEncryptionKey(passKey, newEntry.Salt); @@ -377,7 +377,7 @@ public class Manifest writer.Write(jsonAccount); } - if (Encrypted) + if (encrypt) { File.WriteAllText(Path.Combine(Program.SteamGuardPath, newEntry.Filename), Convert.ToBase64String(ms.ToArray())); } diff --git a/Program.cs b/Program.cs index f74ece3..17253ec 100644 --- a/Program.cs +++ b/Program.cs @@ -185,7 +185,10 @@ public static class Program for (int i = 0; i < SteamGuardAccounts.Length; i++) { var account = SteamGuardAccounts[i]; - bool success = Manifest.SaveAccount(account, true, newPassKey, Manifest.GetRandomSalt(), Manifest.GetInitializationVector()); + var salt = Manifest.GetRandomSalt(); + var iv = Manifest.GetInitializationVector(); + if (Program.Verbose) Console.WriteLine($"New encryption info: pass: {newPassKey}, salt: {salt}, iv: {iv}"); + bool success = Manifest.SaveAccount(account, true, newPassKey, salt, iv); if (Verbose) Console.WriteLine("Encrypted {0}: {1}", account.AccountName, success); } } From 0b0331bcc0d6bf1aec345121a290479535baec6d Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 23 Aug 2016 13:51:59 -0400 Subject: [PATCH 16/19] removed unnneeded verbosity --- Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Program.cs b/Program.cs index 17253ec..d49de6f 100644 --- a/Program.cs +++ b/Program.cs @@ -187,7 +187,6 @@ public static class Program var account = SteamGuardAccounts[i]; var salt = Manifest.GetRandomSalt(); var iv = Manifest.GetInitializationVector(); - if (Program.Verbose) Console.WriteLine($"New encryption info: pass: {newPassKey}, salt: {salt}, iv: {iv}"); bool success = Manifest.SaveAccount(account, true, newPassKey, salt, iv); if (Verbose) Console.WriteLine("Encrypted {0}: {1}", account.AccountName, success); } From 3a0c63b58940dd4bd306b711e0d7520bb7b7751c Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 23 Aug 2016 13:58:00 -0400 Subject: [PATCH 17/19] made Encrypt() and Decrypt() actions return success boolean --- Program.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Program.cs b/Program.cs index d49de6f..15cb861 100644 --- a/Program.cs +++ b/Program.cs @@ -103,10 +103,10 @@ public static class Program GenerateCode(user); break; case "encrypt": // Can also be used to change passkey - Encrypt(); + Console.WriteLine(Encrypt()); break; case "decrypt": - Decrypt(); + Console.WriteLine(Decrypt()); break; case "setup": throw new NotSupportedException(); @@ -165,7 +165,7 @@ public static class Program Console.WriteLine("error: No Steam accounts found in {0}", SteamGuardAccounts); } - static void Encrypt() + static bool Encrypt() { if (Verbose) Console.WriteLine("Opening manifest..."); Manifest = Manifest.GetManifest(true); @@ -189,10 +189,12 @@ public static class Program var iv = Manifest.GetInitializationVector(); bool success = Manifest.SaveAccount(account, true, newPassKey, salt, iv); if (Verbose) Console.WriteLine("Encrypted {0}: {1}", account.AccountName, success); + if (!success) return false; } + return true; } - static void Decrypt() + static bool Decrypt() { if (Verbose) Console.WriteLine("Opening manifest..."); Manifest = Manifest.GetManifest(true); @@ -205,7 +207,7 @@ public static class Program else { if (Verbose) Console.WriteLine("Decryption not required."); - return; + return true; } for (int i = 0; i < SteamGuardAccounts.Length; i++) @@ -213,6 +215,8 @@ public static class Program var account = SteamGuardAccounts[i]; bool success = Manifest.SaveAccount(account, false); if (Verbose) Console.WriteLine("Decrypted {0}: {1}", account.AccountName, success); + if (!success) return false; } + return true; } } From c8a863f09846e970e119b44d6e61e6fdb0572d08 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 23 Aug 2016 13:59:10 -0400 Subject: [PATCH 18/19] removed some commented code --- Manifest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Manifest.cs b/Manifest.cs index 255b4f6..bb250ba 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -305,7 +305,6 @@ public class Manifest public bool SaveAccount(SteamGuardAccount account, bool encrypt, string passKey = null, string salt = null, string iV = null) { if (encrypt && (String.IsNullOrEmpty(passKey) || String.IsNullOrEmpty(salt) || String.IsNullOrEmpty(iV))) return false; - //if (!encrypt && this.Encrypted) return false; string jsonAccount = JsonConvert.SerializeObject(account); @@ -337,7 +336,7 @@ public class Manifest } bool wasEncrypted = this.Encrypted; - this.Encrypted = encrypt;// || this.Encrypted; + this.Encrypted = encrypt; if (!this.Save()) { From 64797e77a9a533bd93ddfe178cc977150505edfc Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Tue, 23 Aug 2016 14:49:07 -0400 Subject: [PATCH 19/19] fixed extra text at the end when writing to a previously encrypted maFile --- Manifest.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Manifest.cs b/Manifest.cs index bb250ba..c71a01f 100644 --- a/Manifest.cs +++ b/Manifest.cs @@ -368,7 +368,11 @@ public class Manifest } else { - stream = File.OpenWrite(Path.Combine(Program.SteamGuardPath, newEntry.Filename)); + // An unencrypted maFile is shorter than the encrypted version, + // so when an unencrypted maFile gets written this way, the file does not get wiped + // leaving encrypted text after the final } bracket. Deleting and recreating the file fixes this. + File.Delete(Path.Combine(Program.SteamGuardPath, newEntry.Filename)); + stream = File.OpenWrite(Path.Combine(Program.SteamGuardPath, newEntry.Filename)); // open or create } using (StreamWriter writer = new StreamWriter(stream))