From e52f2414227473babda8c4abbb79f435a1dc501d Mon Sep 17 00:00:00 2001 From: Pablu23 <43807157+Pablu23@users.noreply.github.com> Date: Tue, 10 Jan 2023 22:19:04 +0100 Subject: [PATCH] Add project files. --- .idea/.idea.EncryptFolder/.idea/.gitignore | 13 + .idea/.idea.EncryptFolder/.idea/encodings.xml | 4 + .../.idea.EncryptFolder/.idea/indexLayout.xml | 8 + .idea/.idea.EncryptFolder/.idea/misc.xml | 6 + EncryptFolder.sln | 16 + EncryptFolder/EncryptFolder.csproj | 10 + EncryptFolder/Program.cs | 627 ++++++++++++++++++ global.json | 7 + 8 files changed, 691 insertions(+) create mode 100644 .idea/.idea.EncryptFolder/.idea/.gitignore create mode 100644 .idea/.idea.EncryptFolder/.idea/encodings.xml create mode 100644 .idea/.idea.EncryptFolder/.idea/indexLayout.xml create mode 100644 .idea/.idea.EncryptFolder/.idea/misc.xml create mode 100644 EncryptFolder.sln create mode 100644 EncryptFolder/EncryptFolder.csproj create mode 100644 EncryptFolder/Program.cs create mode 100644 global.json diff --git a/.idea/.idea.EncryptFolder/.idea/.gitignore b/.idea/.idea.EncryptFolder/.idea/.gitignore new file mode 100644 index 0000000..3963e0a --- /dev/null +++ b/.idea/.idea.EncryptFolder/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/modules.xml +/projectSettingsUpdater.xml +/.idea.EncryptFolder.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.EncryptFolder/.idea/encodings.xml b/.idea/.idea.EncryptFolder/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.EncryptFolder/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.EncryptFolder/.idea/indexLayout.xml b/.idea/.idea.EncryptFolder/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.EncryptFolder/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.EncryptFolder/.idea/misc.xml b/.idea/.idea.EncryptFolder/.idea/misc.xml new file mode 100644 index 0000000..1d8c84d --- /dev/null +++ b/.idea/.idea.EncryptFolder/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/EncryptFolder.sln b/EncryptFolder.sln new file mode 100644 index 0000000..07fbdac --- /dev/null +++ b/EncryptFolder.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EncryptFolder", "EncryptFolder\EncryptFolder.csproj", "{EB82D4BD-71E7-4A0D-8183-AAE87EE293F8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EB82D4BD-71E7-4A0D-8183-AAE87EE293F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB82D4BD-71E7-4A0D-8183-AAE87EE293F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB82D4BD-71E7-4A0D-8183-AAE87EE293F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB82D4BD-71E7-4A0D-8183-AAE87EE293F8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/EncryptFolder/EncryptFolder.csproj b/EncryptFolder/EncryptFolder.csproj new file mode 100644 index 0000000..b9de063 --- /dev/null +++ b/EncryptFolder/EncryptFolder.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/EncryptFolder/Program.cs b/EncryptFolder/Program.cs new file mode 100644 index 0000000..18bc700 --- /dev/null +++ b/EncryptFolder/Program.cs @@ -0,0 +1,627 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace EncryptFolder +{ + public static class Program + { + private static string _workDir = Directory.GetCurrentDirectory(); + private static string _encryptFolder = Path.Combine(_workDir, "private"); + private static string _password = ""; + + public static void Main() + { + if (Directory.Exists(_encryptFolder)) + { + MenuDecrypt(); + } + else + { + MenuEncrypt(); + } + } + + private static void MenuDecrypt() + { + Console.Write("Enter Password: "); + string? password = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(password)) + { + Console.WriteLine("No password was Input.\nClosing Program..."); + Environment.Exit(0); + } + _password = password; + StartDecryptProc(); + } + + private static void MenuEncrypt() + { + var files = Directory.GetFiles(_workDir).ToList(); + + // Get Path of Executable + // Exe File is supposed to be in the same Folder as the Files that should get encrypted, + // but should obviously not be encrypted itself + string? execPath = Environment.ProcessPath; + string? dllPath = Assembly.GetEntryAssembly()?.Location; + + // If dll and or exe Path are in the files List, remove them. + // If not remove all exe and dll files for safety + if (!string.IsNullOrWhiteSpace(dllPath)) + files.RemoveAll(x => x == dllPath); + if (string.IsNullOrWhiteSpace(execPath)) + files.RemoveAll(f => Path.GetExtension(f) == "exe" || Path.GetExtension(f) == "dll"); + else + files.RemoveAll(x => x == execPath); + + foreach (string file in files) + { + Console.WriteLine(file); + } + + Console.WriteLine("Are you sure you want to Encrypt these Files?\n[Y]es\n[N]o"); + string? input = Console.ReadLine(); + switch (input?.ToLower()) + { + case "y" or "yes" or "j": + break; + + case "n" or "no": + Console.WriteLine("Exiting..."); + Environment.Exit(0); + break; + + default: + Console.WriteLine("Wrong Input.\nClosing Program..."); + Environment.Exit(0); + break; + } + + Console.Write("Enter Password: "); + string? password = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(password)) + { + Console.WriteLine("No password was Input.\nClosing Program..."); + Environment.Exit(0); + } + _password = password; + StartEncryptProc(files.ToArray()); + } + + private static void StartEncryptProc(string[] files) + { + // Creates private Folder + Directory.CreateDirectory(_encryptFolder); + + // Creates List for tasks that are going to be run + // Every file has its own Task + var tasks = new List(); + + for (int i = 0; i < files.Length; i++) + { + // Because of a racing condition and scoping issues the variables need to be set again here + // in order to work correctly + string file = files[i]; + int index = i; + + // Run the Encrypt Process and add the running task to the tasks list + tasks.Add(Task.Run(async () => await EncryptProc(file, index))); + } + + try + { + var cancel = new CancellationTokenSource(); + Task.Run(() => UpdateConsole(cancel.Token)); + + // Wait for all Tasks to complete + Task.WaitAll(tasks.ToArray()); + + // Cancel the UpdateConsole + cancel.Cancel(); + } + catch (AggregateException ex) + { + foreach (var inner in ex.InnerExceptions) + { + Console.WriteLine($"Caught AggregateException in Task: " + inner.Message); + } + } + catch (Exception ex) + { + Console.WriteLine($"Caught Exception in Main: " + ex.Message); + } + } + + private static Task EncryptProc(string path, int index) + { + // Obfuscate filename and add extension .enc + string fileName = $"{index}.enc"; + + // Create Path in the Private folder for storing of file + string newPath = Path.Combine(_encryptFolder, fileName); + + // Creates 128 Bit Key from password + // And retrieves random Salt used for it + byte[] pwd = CreateKey(_password, out byte[] salt); + + var fileInfo = new FileInfo(path); + + bool withInfo = false; + + if (fileInfo.Length >= 30_000_000) + { + withInfo = true; + } + + byte[] pwdHash = CreateHash(pwd); + + // Encrypt the File + EncryptFile(path, newPath, pwd, salt, pwdHash, withInfo); + + // Delete the Unencrypted file + File.Delete(path); + + return Task.CompletedTask; + } + + private static void EncryptFile(string path, string outFile, byte[] key, byte[] salt, byte[] pwdHash, bool withInfo) + { + // Setup AES + var aes = Aes.Create(); + aes.Key = key; + + // Create Encryptor with the Key and the AES Genereratev IV + var transform = aes.CreateEncryptor(key, aes.IV); + + // Gets IV Length in bit + int lIv = aes.IV.Length; + byte[] lenIV = BitConverter.GetBytes(lIv); + + // Encrypt the Filename and get Length of Bytes + string fileName = Path.GetFileName(path); + + byte[] name = EncryptString(key, aes.IV, fileName); + int lName = name.Length; + byte[] lenName = BitConverter.GetBytes(lName); + + // Gets Salt Length + int lSalt = salt.Length; + byte[] lenSalt = BitConverter.GetBytes(lSalt); + + // Gets PwdHash Length + int lHash = pwdHash.Length; + byte[] lenHash = BitConverter.GetBytes(lHash); + + using (var outFs = new FileStream(outFile, FileMode.Create)) + { + /* + * 0 - 3 = Byte IV Length + * 4 - 7 = Byte Name Length + * 8 - 11 = Salt Length + * 12 - 15 = Hash Length + * 16 - IV Length = IV + * IV Length - Name Length = Name Obfuscated + * Name Length - Salt Length = Salt + * Salt Length - Hash Length = Hash + */ + + // Write the IV Length to Header + outFs.Write(lenIV, 0, 4); + // Write Filename Length to Header + outFs.Write(lenName, 0, 4); + // Write Salt Length to Header + outFs.Write(lenSalt, 0, 4); + // Write Hash Length to Header + outFs.Write(lenHash, 0, 4); + + // After IV Length Bit + // Write the IV itself + outFs.Write(aes.IV, 0, lIv); + // Write the Filename (Encrypted) + outFs.Write(name, 0, lName); + // Write the Password Salt + outFs.Write(salt, 0, lSalt); + // Write teh Password Hash + outFs.Write(pwdHash, 0, lHash); + + using (var outStreamEncrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write)) + { + int count; + int offset = 0; + + // BlockSizeBytes is arbitrary size + int blockSizeBytes = aes.BlockSize / 8; + byte[] data = new byte[blockSizeBytes]; + + using (var inFs = new FileStream(path, FileMode.Open)) + { + // Go through every ByteBlock in File + do + { + long size = inFs.Length; + + // Read bytes into data Array and set count to how many bytes were actually read + count = inFs.Read(data, 0, blockSizeBytes); + + offset += count; + + if (withInfo) + { + Info.AddOrUpdate(fileName, new Info() { BytesSize = size, BytesRead = offset, NewMessage = true }, (_, inf) => + { + inf.BytesRead = offset; + inf.NewMessage = true; + return inf; + }); + } + + // Encrypt bytes and Write it to new File + outStreamEncrypted.Write(data, 0, count); + + // As long as there are more bytes to be read + } while (count > 0); + } + // Needed, but dont know what it does + outStreamEncrypted.FlushFinalBlock(); + } + } + } + + private static void StartDecryptProc() + { + // Create task List + var tasks = new List(); + + // Get the remaining encrypted Files + var files = Directory.GetFiles(_encryptFolder).ToList(); + files.RemoveAll(f => Path.GetExtension(f) != ".enc"); + + // Foreach encrypted File start a Task which decrypts that file + foreach (string file in files) + { + // Because of a racing condition and scoping issues the variable needs to be set again here + // in order to work correctly + string path = file; + tasks.Add(Task.Run(async () => await DecryptProc(path))); + } + + try + { + var cancel = new CancellationTokenSource(); + Task.Run(() => UpdateConsole(cancel.Token)); + + // Wait for all Tasks to complete + Task.WaitAll(tasks.ToArray()); + + cancel.Cancel(); + + // Delete the now unused Folder + Directory.Delete(_encryptFolder, false); + } + catch (AggregateException ex) + { + foreach (var inner in ex.InnerExceptions) + { + Console.WriteLine($"Caught AggregateException in Task: " + inner.Message); + } + } + catch (Exception ex) + { + Console.WriteLine($"Caught Exception in Main: " + ex.Message); + } + } + + private static Task DecryptProc(string path) + { + var fileInfo = new FileInfo(path); + + bool withInfo = false; + + if (fileInfo.Length >= 30_000_000) + { + withInfo = true; + } + + // Create shared FileStream + using (var fs = new FileStream(path, FileMode.Open)) + { + // Get Header Metadata and Header Length + (int headerLen, int ivLen, int nameLen, int saltLen, int hashLen) = GetFileHeader(fs); + + // Get Header Data and Point of where the Cipher starts + (int startCipher, byte[] iv, byte[] name, byte[] salt, byte[] hash) = GetHeaderData(fs, headerLen, ivLen, nameLen, saltLen, hashLen); + + // Create 128 Bit (16 Byte) Key with Password and Salt + byte[] key = CreateKey(_password, salt); + + byte[] generatedHash = CreateHash(key); + + if (!generatedHash.SequenceEqual(hash)) + throw new Exception("Password was incorrect"); + + // Decrypt the Filename + string fileName = DecryptString(key, iv, name); + // Create Output path + string outFile = Path.Combine(_workDir, fileName); + + if (File.Exists(outFile)) + { + outFile = Path.Combine(_workDir, Path.GetRandomFileName()); + outFile = Path.ChangeExtension(outFile, Path.GetExtension(fileName)); + } + + // Decrypt File to output Path + DecryptFile(fs, outFile, startCipher, iv, key, withInfo); + } + + // Delete the Encrypted File + File.Delete(path); + + return Task.CompletedTask; + } + + private static (int headerLen, int ivLen, int nameLen, int saltLen, int hashLen) GetFileHeader(FileStream inFs) + { + // lIV + lName + lSalt + lHash = 16 + int headerLen = 16; + + byte[] lIV = new byte[4]; + byte[] lName = new byte[4]; + byte[] lSalt = new byte[4]; + byte[] lHash = new byte[4]; + + // Read IV Length + inFs.Seek(0, SeekOrigin.Begin); + inFs.Read(lIV, 0, 3); + + // Read Filename Length + inFs.Seek(4, SeekOrigin.Begin); + inFs.Read(lName, 0, 3); + + // Read Salt Length + inFs.Seek(8, SeekOrigin.Begin); + inFs.Read(lSalt, 0, 3); + + inFs.Seek(12, SeekOrigin.Begin); + inFs.Read(lHash, 0, 3); + + // Convert Byte to int Length + int ivLen = BitConverter.ToInt32(lIV, 0); + int nameLen = BitConverter.ToInt32(lName, 0); + int saltLen = BitConverter.ToInt32(lSalt, 0); + int hashLen = BitConverter.ToInt32(lHash, 0); + + return (headerLen, ivLen, nameLen, saltLen, hashLen); + } + + private static (int startCipher, byte[] iv, byte[] name, byte[] salt, byte[] pwdHash) GetHeaderData(FileStream inFs, int headerLen, int ivLen, int nameLen, int saltLen, int hashLen) + { + // Store IV + byte[] iv = new byte[ivLen]; + inFs.Seek(headerLen, SeekOrigin.Begin); + inFs.Read(iv, 0, ivLen); + + // Store Filename + byte[] name = new byte[nameLen]; + inFs.Seek(headerLen + ivLen, SeekOrigin.Begin); + inFs.Read(name, 0, nameLen); + + // Store Salt + byte[] salt = new byte[saltLen]; + inFs.Seek(headerLen + ivLen + nameLen, SeekOrigin.Begin); + inFs.Read(salt, 0, saltLen); + + byte[] hash = new byte[hashLen]; + inFs.Seek(headerLen + ivLen + nameLen + saltLen, SeekOrigin.Begin); + inFs.Read(hash, 0, hashLen); + + // Data starts after Header and Header Data + int startCipher = nameLen + ivLen + saltLen + hashLen + headerLen; + return (startCipher, iv, name, salt, hash); + } + + private static void DecryptFile(FileStream inFs, string outFile, int startCipher, byte[] iv, byte[] key, bool withInfo) + { + // Setup Aes + using var aes = Aes.Create(); + aes.Key = key; + //aes.Padding = PaddingMode.PKCS7; + + // Create Decryptor with the Key and the IV + var transform = aes.CreateDecryptor(key, iv); + + string name = Path.GetFileName(inFs.Name); + long size = inFs.Length; + + using var outFs = new FileStream(outFile, FileMode.CreateNew); + using var outStreamDecrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write); + + int count; + + int overall = 0; + + // Arbitrary Size + int blockSizeBytes = aes.BlockSize / 8; + byte[] data = new byte[blockSizeBytes]; + + // Set Stream position to starting Data Position + inFs.Seek(startCipher, SeekOrigin.Begin); + do + { + // Read Bytes into Data array and get read bytes + count = inFs.Read(data, 0, blockSizeBytes); + + overall += count; + + if (withInfo) + { + Info.AddOrUpdate(name, new Info() { BytesSize = size, BytesRead = overall, NewMessage = true }, (_, inf) => + { + inf.BytesRead = overall; + inf.NewMessage = true; + return inf; + }); + } + + // Decrypts Bytes and writes them to the output file + outStreamDecrypted.Write(data, 0, count); + + // As long as there are more Bytes to read + } while (count > 0); + + // Dont know what this does but its needed + outStreamDecrypted.FlushFinalBlock(); + } + + private static byte[] EncryptString(byte[] key, byte[] iv, string input) + { + if (input == null || input.Length <= 0) + throw new ArgumentNullException(nameof(input)); + if (key == null || key.Length <= 0) + throw new ArgumentNullException(nameof(key)); + if (iv == null || iv.Length <= 0) + throw new ArgumentNullException(nameof(iv)); + + byte[] encrypted; + + // Create an Aes object + // with the specified key and IV. + using (Aes aesAlg = Aes.Create()) + { + aesAlg.Key = key; + aesAlg.IV = iv; + + // Create an encryptor to perform the stream transform. + var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); + + // Create the streams used for encryption. + using var msEncrypt = new MemoryStream(); + using var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write); + using (var swEncrypt = new StreamWriter(csEncrypt)) + { + //Write all data to the stream. + swEncrypt.Write(input); + } + encrypted = msEncrypt.ToArray(); + } + + // Return the encrypted bytes from the memory stream. + return encrypted; + } + + private static string DecryptString(byte[] key, byte[] iv, byte[] input) + { + if (input == null || input.Length <= 0) + throw new ArgumentNullException(nameof(input)); + if (key == null || key.Length <= 0) + throw new ArgumentNullException(nameof(key)); + if (iv == null || iv.Length <= 0) + throw new ArgumentNullException(nameof(iv)); + + // Declare the string used to hold + // the decrypted text. + string plaintext; + + // Create an Aes object + // with the specified key and IV. + using (Aes aesAlg = Aes.Create()) + { + aesAlg.Key = key; + aesAlg.IV = iv; + + // Create a decryptor to perform the stream transform. + ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); + + // Create the streams used for decryption. + using MemoryStream msDecrypt = new MemoryStream(input); + using CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read); + using (StreamReader srDecrypt = new StreamReader(csDecrypt)) + { + // Read the decrypted bytes from the decrypting stream + // and place them in a string. + plaintext = srDecrypt.ReadToEnd(); + } + } + + return plaintext; + } + + private static byte[] CreateHash(byte[] pwd) + { + using var hash = SHA256.Create(); + return hash.ComputeHash(pwd); + } + + private static byte[] GetSalt(int maximumSaltLength) + { + return RandomNumberGenerator.GetBytes(maximumSaltLength); + } + + private const int Iterations = 300; + + private static byte[] CreateKey(string password, out byte[] salt, int keyBytes = 16) + { + salt = GetSalt(32); + var keyGenerator = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(password), salt, Iterations); + return keyGenerator.GetBytes(keyBytes); + } + + private static byte[] CreateKey(string password, byte[] salt, int keyBytes = 16) + { + var keyGenerator = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(password), salt, Iterations); + return keyGenerator.GetBytes(keyBytes); + } + + private static readonly ConcurrentDictionary Info = new ConcurrentDictionary(); + + private static Task UpdateConsole(CancellationToken token) + { + (int _, int top) = Console.GetCursorPosition(); + + var mapping = new Dictionary(); + int highest = 0; + + while (true) + { + if (token.IsCancellationRequested) + return Task.CompletedTask; + + Thread.Sleep(1000); + foreach ((string? key, var value) in Info) + { + if (token.IsCancellationRequested) + return Task.CompletedTask; + + if (!mapping.ContainsKey(key)) + mapping.Add(key, highest += 1); + + if (value.NewMessage) + { + double percent = (double) value.BytesRead / (double) value.BytesSize; + percent *= 100; + + percent = Math.Round(percent, 2); + + Console.SetCursorPosition(0, top + mapping[key]); + Console.WriteLine("[INFO] File {0,-30} {1,-5} % / 100 %", key, percent); + value.NewMessage = false; + } + } + } + } + } + + public class Info + { + public long BytesSize { get; set; } + public int BytesRead { get; set; } + public bool NewMessage { get; set; } + } +} \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 0000000..f443bd4 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "6.0", + "rollForward": "latestMajor", + "allowPrerelease": true + } +} \ No newline at end of file