From aaecd43e2d963406c9dca0b50b7958283710851c Mon Sep 17 00:00:00 2001 From: LoRd_MuldeR Date: Wed, 12 Jun 2024 17:41:53 +0200 Subject: [PATCH] GUI: Hide the options for "legacy" compatibility mode by default + some improvements on error handling. --- README.md | 5 ++- gui/App.config | 2 +- gui/Process/SlunkCryptRunner.cs | 47 +++++++++++++++++------- gui/SlunkCryptGUI.csproj | 1 + gui/SlunkCryptGUI.xaml.cs | 58 +++++++++++++++++------------- gui/Utilities/ApplicationConfig.cs | 19 ++++++++-- gui/Utilities/DictionaryHelper.cs | 18 ++++++++++ 7 files changed, 109 insertions(+), 41 deletions(-) create mode 100644 gui/Utilities/DictionaryHelper.cs diff --git a/README.md b/README.md index 9da1274..c1c8e8f 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,9 @@ The following settings can be adjusted in the `slunkcrypt-gui.exe.config` config - **`KeepIncompleteFiles`:** If set to `true`, incomplete or corrupted output files will *not* be deleted — default value: `false`. +- **`LegacyCompat`:** + If set to `1`, show options to enable “legacy” compatibility-mode in the GUI; if set to `2`, select “legacy” compatibility-mode by default — default value: `0`. + Command-line Usage ================== @@ -132,7 +135,7 @@ The following environment variables may be used: Specifies the number of worker threads to use. By default, SlunkCrypt detects the number of available processors and creates one thread for each processor. - **`SLUNK_LEGACY_COMPAT`**: - If set to a *non-zero* value, enables "legacy" compatibility-mode, required to decrypt files encrypted with SlunkCrypt version 1.2.x or older. + If set to a *non-zero* value, enables “legacy” compatibility-mode, required to decrypt files encrypted with SlunkCrypt version 1.2.x or older. - **`SLUNK_DEBUG_LOGGING`**: If set to a *non-zero* value, enables additional logging output to the syslog (Unix-like) or to the debugger (Windows). This is intended for debugging purposes only! diff --git a/gui/App.config b/gui/App.config index 1ede395..0dbe71c 100644 --- a/gui/App.config +++ b/gui/App.config @@ -7,6 +7,6 @@ - + diff --git a/gui/Process/SlunkCryptRunner.cs b/gui/Process/SlunkCryptRunner.cs index fe4ddc5..c6ca8fb 100644 --- a/gui/Process/SlunkCryptRunner.cs +++ b/gui/Process/SlunkCryptRunner.cs @@ -19,6 +19,8 @@ namespace com.muldersoft.slunkcrypt.gui.process { public enum Mode { Encrypt, Decrypt } + public enum Error { Checksum, Password } + public struct SlunkOptions { public SlunkOptions(bool keepIncompleteFiles, int threadCount, bool enableLegacyCompat) @@ -40,7 +42,14 @@ namespace com.muldersoft.slunkcrypt.gui.process private const bool ENABLE_DEBUG_LOGGING = false; #endif - private static readonly Regex RX_PROGRESS = new Regex(@"(\d+)\.(\d)%", RegexOptions.Compiled); + private static readonly Regex RX_PROGRESS = new Regex(@"(\d+)\.(\d)%", RegexOptions.Compiled | RegexOptions.CultureInvariant); + + private static readonly IReadOnlyDictionary RX_ERROR = new Dictionary + { + { Error.Checksum, new Regex(@"\bChecksum\s+mismatch\s+detected\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) }, + { Error.Password, new Regex(@"\bThe\s+given\s+passphrase\s+is\s+forbidden\s+as\s+a\s+precautionary\s+measure\b", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) } + } + .ToReadOnlyDictionary(); private readonly FileStream m_executableFile; @@ -63,12 +72,15 @@ namespace com.muldersoft.slunkcrypt.gui.process { throw new ArgumentException("Invalid SlunkCrypt parameters!"); } - Dictionary environmentVariables = new Dictionary(); - environmentVariables.Add("SLUNK_PASSPHRASE", password); - environmentVariables.Add("SLUNK_KEEP_INCOMPLETE", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.keepIncompleteFiles : false))); - environmentVariables.Add("SLUNK_THREADS", Convert.ToString(Math.Max(0, Math.Min(32, options.HasValue ? options.Value.threadCount : 0)))); - environmentVariables.Add("SLUNK_LEGACY_COMPAT", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.enableLegacyCompat : false))); - environmentVariables.Add("SLUNK_DEBUG_LOGGING", Convert.ToString(Convert.ToInt32(ENABLE_DEBUG_LOGGING))); + Dictionary environmentVariables = new Dictionary + { + { "SLUNK_PASSPHRASE", password }, + { "SLUNK_KEEP_INCOMPLETE", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.keepIncompleteFiles : false)) }, + { "SLUNK_THREADS", Convert.ToString(Math.Max(0, Math.Min(32, options.HasValue ? options.Value.threadCount : 0))) }, + { "SLUNK_LEGACY_COMPAT", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.enableLegacyCompat : false)) }, + { "SLUNK_DEBUG_LOGGING", Convert.ToString(Convert.ToInt32(ENABLE_DEBUG_LOGGING)) } + }; + ErrorState = null; return await ExecAsnyc(m_executableFile.Name, new string[] { GetCommandString(mode), inputFile, outputFile }, Path.GetDirectoryName(outputFile), environmentVariables); } @@ -82,23 +94,34 @@ namespace com.muldersoft.slunkcrypt.gui.process catch { } } + public Error? ErrorState { get; private set; } = null; + // ============================================================================= // Internal methods // ============================================================================= protected override double ParseProgressString(ref string currentLine, bool stream) { - double temp, result = double.NaN; int index = int.MaxValue; - Match match = RX_PROGRESS.Match(currentLine); - while (match.Success) + double temp = double.NaN, result = double.NaN; + if (!ErrorState.HasValue) + { + foreach (KeyValuePair errorRegex in RX_ERROR) + { + if (errorRegex.Value.IsMatch(currentLine)) + { + ErrorState = errorRegex.Key; + break; + } + } + } + for (Match match = RX_PROGRESS.Match(currentLine); match.Success; match = match.NextMatch()) { if (TryParseProgressValue(match, out temp)) { - result = temp; + result = double.IsNaN(result) ? temp : Math.Max(result, temp); } index = Math.Min(index, match.Index); - match = RX_PROGRESS.Match(currentLine, match.Index + match.Length); } if (index != int.MaxValue) { diff --git a/gui/SlunkCryptGUI.csproj b/gui/SlunkCryptGUI.csproj index 75d4819..08d3e98 100644 --- a/gui/SlunkCryptGUI.csproj +++ b/gui/SlunkCryptGUI.csproj @@ -82,6 +82,7 @@ PasswordToggleBox.xaml + diff --git a/gui/SlunkCryptGUI.xaml.cs b/gui/SlunkCryptGUI.xaml.cs index e35406e..a848242 100644 --- a/gui/SlunkCryptGUI.xaml.cs +++ b/gui/SlunkCryptGUI.xaml.cs @@ -49,7 +49,7 @@ namespace com.muldersoft.slunkcrypt.gui private volatile int m_isInitialized = 0; private volatile ModeOfOperation m_modeOfOperation = (ModeOfOperation)(-1); - private volatile bool m_busyFlag = false, m_checksumError = false, m_processReceived = false, m_disableAnimation = false; + private volatile bool m_busyFlag = false, m_processReceived = false, m_disableAnimation = false; private volatile SlunkCryptRunner m_processRunner = null; private uint? m_menuId_disableAnimation = null, m_menuId_enableExpertMode = null; @@ -68,6 +68,10 @@ namespace com.muldersoft.slunkcrypt.gui m_dispatcherTimer.Interval = TimeSpan.FromMilliseconds(331); m_logFileReadOnly = new ReadOnlyObservableCollection(m_logFile); m_disableAnimation = m_config.DisableBusyIndicator; + if (m_config.LegacyCompat < 1) + { + Checkbox_Encrypt_LegacyCompat.Visibility = Checkbox_Decrypt_LegacyCompat.Visibility = Visibility.Collapsed; + } } // ============================================================================= @@ -125,7 +129,7 @@ namespace com.muldersoft.slunkcrypt.gui { Hint_SoftwareRendering.Visibility = Visibility.Visible; } - if (m_config.LegacyCompat) + if (m_config.LegacyCompat > 1) { Checkbox_Encrypt_LegacyCompat.IsChecked = Checkbox_Decrypt_LegacyCompat.IsChecked = true; } @@ -273,10 +277,6 @@ namespace com.muldersoft.slunkcrypt.gui private void Process_OutputAvailable(string line, bool stderr) { AppendLogFile(line); - if (line.IndexOf("Checksum mismatch detected!", StringComparison.OrdinalIgnoreCase) >= 0) - { - m_checksumError = true; - } } private void Porcess_ProgressChanged(double progress) @@ -522,7 +522,7 @@ namespace com.muldersoft.slunkcrypt.gui private async Task ValidatePassword(string inputFilePath, string outputFilePath, PasswordToggleBox passwordEdit, CheckBox legacyCheckBox, SlunkProcessor processor, bool checkStrongPasswd) { - bool enableLegacyCompat = legacyCheckBox.IsChecked.GetValueOrDefault(); + bool enableLegacyCompat = (m_config.LegacyCompat > 0) && legacyCheckBox.IsChecked.GetValueOrDefault(m_config.LegacyCompat > 1); string passwordStr; if (string.IsNullOrEmpty(passwordStr = passwordEdit.Password) || (passwordStr.Length < MIN_PASSWD_LENGTH)) { @@ -548,7 +548,7 @@ namespace com.muldersoft.slunkcrypt.gui return; } } - if (enableLegacyCompat && (!m_config.LegacyCompat)) + if (enableLegacyCompat && (m_config.LegacyCompat < 2)) { if (MessageBox.Show(this, "Legacy compat-mode should not be used to encrypt new files!", "Legacy Compatibility", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) != MessageBoxResult.OK) { @@ -567,7 +567,7 @@ namespace com.muldersoft.slunkcrypt.gui ResetKeyboardFocus(this); SetProgress(double.PositiveInfinity); ClearLogFile(); - Button_Decrypt_Toggle.IsChecked = Button_Encrypt_Toggle.IsChecked = m_checksumError = m_processReceived = false; + Button_Decrypt_Toggle.IsChecked = Button_Encrypt_Toggle.IsChecked = m_processReceived = false; if (!await processor(inputFile, outputFile, password, enableLegacyCompat)) { if (!m_config.KeepIncompleteFiles) @@ -583,10 +583,10 @@ namespace com.muldersoft.slunkcrypt.gui private async Task Encrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat) { SetStatus("Please wait while the encryption process is initializing..."); - int? exitCode = await RunProcess(SlunkCryptRunner.Mode.Encrypt, inputFile, outputFile, password, enableLegacyCompat); - if (exitCode.HasValue) + Tuple result = await RunProcess(SlunkCryptRunner.Mode.Encrypt, inputFile, outputFile, password, enableLegacyCompat); + if (!ReferenceEquals(result, null)) { - if (exitCode.Value == 0) + if (result.Item1 == 0) { SetProgress(1); SetStatus("Completed: The file has been encrypted successfully.", Status.Success); @@ -594,8 +594,16 @@ namespace com.muldersoft.slunkcrypt.gui } else { + switch (result.Item2) + { + case SlunkCryptRunner.Error.Password: + SetStatus("Error: The specified passphrase is forbidden! (contained in OWASP database)", Status.Failure); + break; + default: + SetStatus("Error: Failed to enecrypt the file. Please see the log file for details!", Status.Failure); + break; + } SetProgress(1, true); - SetStatus("Error: Failed to enecrypt the file. Please see the log file for details!", Status.Failure); SystemSounds.Hand.Play(); } return true; @@ -606,10 +614,10 @@ namespace com.muldersoft.slunkcrypt.gui private async Task Decrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat) { SetStatus("Please wait while the decryption process is initializing..."); - int? exitCode = await RunProcess(SlunkCryptRunner.Mode.Decrypt, inputFile, outputFile, password, enableLegacyCompat); - if (exitCode.HasValue) + Tuple result = await RunProcess(SlunkCryptRunner.Mode.Decrypt, inputFile, outputFile, password, enableLegacyCompat); + if (!ReferenceEquals(result, null)) { - if (exitCode.Value == 0) + if (result.Item1 == 0) { SetStatus("Completed: The file has been decrypted successfully (checksum is correct).", Status.Success); SetProgress(1); @@ -617,13 +625,14 @@ namespace com.muldersoft.slunkcrypt.gui } else { - if (m_checksumError) + switch (result.Item2) { - SetStatus("Error: Checksum mismatch detected! Wrong passphrase or corrupted file?", Status.Failure); - } - else - { - SetStatus("Error: Failed to decrypt the file. Please see the log file for details!", Status.Failure); + case SlunkCryptRunner.Error.Checksum: + SetStatus("Error: Checksum mismatch detected! Wrong passphrase or corrupted file?", Status.Failure); + break; + default: + SetStatus("Error: Failed to decrypt the file. Please see the log file for details!", Status.Failure); + break; } SetProgress(1, true); SystemSounds.Hand.Play(); @@ -633,7 +642,7 @@ namespace com.muldersoft.slunkcrypt.gui return false; } - private async Task RunProcess(SlunkCryptRunner.Mode mode, string inputFile, string outputFile, string password, bool enableLegacyCompat) + private async Task> RunProcess(SlunkCryptRunner.Mode mode, string inputFile, string outputFile, string password, bool enableLegacyCompat) { if (!ReferenceEquals(m_processRunner, null)) { @@ -647,7 +656,8 @@ namespace com.muldersoft.slunkcrypt.gui m_processRunner.OutputAvailable += Process_OutputAvailable; m_processRunner.ProgressChanged += Porcess_ProgressChanged; SetProcessPriority(ProcessPriorityClass.AboveNormal); - return await m_processRunner.ExecuteAsync(mode, inputFile, outputFile, password, options); + int exitCode = await m_processRunner.ExecuteAsync(mode, inputFile, outputFile, password, options); + return Tuple.Create(exitCode, m_processRunner.ErrorState); } } catch (ProcessRunner.ProcessStartException err) diff --git a/gui/Utilities/ApplicationConfig.cs b/gui/Utilities/ApplicationConfig.cs index c725a09..65d53f5 100644 --- a/gui/Utilities/ApplicationConfig.cs +++ b/gui/Utilities/ApplicationConfig.cs @@ -34,15 +34,15 @@ namespace com.muldersoft.slunkcrypt.gui.utils { get { - return ComputeIfAbsent("ThreadCount", (key) => AppConfHelper.GetConfigValueAsInt32(key).GetValueOrDefault(0)); + return ComputeIfAbsent("ThreadCount", (key) => Clamp(0, AppConfHelper.GetConfigValueAsInt32(key).GetValueOrDefault(0), 32)); } } - public bool LegacyCompat + public int LegacyCompat { get { - return ComputeIfAbsent("LegacyCompat", (key) => AppConfHelper.GetConfigValueAsBool(key).GetValueOrDefault(false)); + return ComputeIfAbsent("LegacyCompat", (key) => Clamp(0, AppConfHelper.GetConfigValueAsInt32(key).GetValueOrDefault(0), 2)); } } @@ -60,6 +60,11 @@ namespace com.muldersoft.slunkcrypt.gui.utils return Convert.ToBoolean(m_cache.GetOrAdd(name, (key) => Convert.ToInt32(valueFactory(key)))); } + protected static int Clamp(int min, int value, int max) + { + return (value < min) ? min : ((value > max) ? max : value); + } + // ============================================================================= // Helper class // ============================================================================= @@ -116,6 +121,14 @@ namespace com.muldersoft.slunkcrypt.gui.utils { return result; } + else + { + bool boolean; + if (bool.TryParse(value.Trim(), out boolean)) + { + return Convert.ToInt32(boolean); + } + } } return null; } diff --git a/gui/Utilities/DictionaryHelper.cs b/gui/Utilities/DictionaryHelper.cs new file mode 100644 index 0000000..6ca35e1 --- /dev/null +++ b/gui/Utilities/DictionaryHelper.cs @@ -0,0 +1,18 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace com.muldersoft.slunkcrypt.gui.utils +{ + public static class DictionaryHelper + { + public static IReadOnlyDictionary ToReadOnlyDictionary(this IDictionary dict) + { + return (dict is ReadOnlyDictionary) ? ((ReadOnlyDictionary)dict) : new ReadOnlyDictionary(dict); + } + } +}