GUI: Hide the options for "legacy" compatibility mode by default + some improvements on error handling.

This commit is contained in:
LoRd_MuldeR 2024-06-12 17:41:53 +02:00
parent cc3f9da1d2
commit aaecd43e2d
Signed by: mulder
GPG Key ID: 2B5913365F57E03F
7 changed files with 109 additions and 41 deletions

View File

@ -63,6 +63,9 @@ The following settings can be adjusted in the `slunkcrypt-gui.exe.config` config
- **`KeepIncompleteFiles`:** - **`KeepIncompleteFiles`:**
If set to `true`, incomplete or corrupted output files will *not* be deleted — default value: `false`. 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 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. 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`**: - **`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`**: - **`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! 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!

View File

@ -7,6 +7,6 @@
<add key="DisableBusyIndicator" value="false"/> <add key="DisableBusyIndicator" value="false"/>
<add key="ThreadCount" value="0"/> <add key="ThreadCount" value="0"/>
<add key="KeepIncompleteFiles" value="false"/> <add key="KeepIncompleteFiles" value="false"/>
<add key="LegacyCompat" value="false"/> <add key="LegacyCompat" value="0"/>
</appSettings> </appSettings>
</configuration> </configuration>

View File

@ -19,6 +19,8 @@ namespace com.muldersoft.slunkcrypt.gui.process
{ {
public enum Mode { Encrypt, Decrypt } public enum Mode { Encrypt, Decrypt }
public enum Error { Checksum, Password }
public struct SlunkOptions public struct SlunkOptions
{ {
public SlunkOptions(bool keepIncompleteFiles, int threadCount, bool enableLegacyCompat) 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; private const bool ENABLE_DEBUG_LOGGING = false;
#endif #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<Error, Regex> RX_ERROR = new Dictionary<Error, Regex>
{
{ 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; private readonly FileStream m_executableFile;
@ -63,12 +72,15 @@ namespace com.muldersoft.slunkcrypt.gui.process
{ {
throw new ArgumentException("Invalid SlunkCrypt parameters!"); throw new ArgumentException("Invalid SlunkCrypt parameters!");
} }
Dictionary<string, string> environmentVariables = new Dictionary<string, string>(); Dictionary<string, string> environmentVariables = new Dictionary<string, string>
environmentVariables.Add("SLUNK_PASSPHRASE", password); {
environmentVariables.Add("SLUNK_KEEP_INCOMPLETE", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.keepIncompleteFiles : false))); { "SLUNK_PASSPHRASE", password },
environmentVariables.Add("SLUNK_THREADS", Convert.ToString(Math.Max(0, Math.Min(32, options.HasValue ? options.Value.threadCount : 0)))); { "SLUNK_KEEP_INCOMPLETE", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.keepIncompleteFiles : false)) },
environmentVariables.Add("SLUNK_LEGACY_COMPAT", Convert.ToString(Convert.ToInt32(options.HasValue ? options.Value.enableLegacyCompat : false))); { "SLUNK_THREADS", Convert.ToString(Math.Max(0, Math.Min(32, options.HasValue ? options.Value.threadCount : 0))) },
environmentVariables.Add("SLUNK_DEBUG_LOGGING", Convert.ToString(Convert.ToInt32(ENABLE_DEBUG_LOGGING))); { "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); 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 { } catch { }
} }
public Error? ErrorState { get; private set; } = null;
// ============================================================================= // =============================================================================
// Internal methods // Internal methods
// ============================================================================= // =============================================================================
protected override double ParseProgressString(ref string currentLine, bool stream) protected override double ParseProgressString(ref string currentLine, bool stream)
{ {
double temp, result = double.NaN;
int index = int.MaxValue; int index = int.MaxValue;
Match match = RX_PROGRESS.Match(currentLine); double temp = double.NaN, result = double.NaN;
while (match.Success) if (!ErrorState.HasValue)
{
foreach (KeyValuePair<Error, Regex> 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)) if (TryParseProgressValue(match, out temp))
{ {
result = temp; result = double.IsNaN(result) ? temp : Math.Max(result, temp);
} }
index = Math.Min(index, match.Index); index = Math.Min(index, match.Index);
match = RX_PROGRESS.Match(currentLine, match.Index + match.Length);
} }
if (index != int.MaxValue) if (index != int.MaxValue)
{ {

View File

@ -82,6 +82,7 @@
<DependentUpon>PasswordToggleBox.xaml</DependentUpon> <DependentUpon>PasswordToggleBox.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Properties\_Version.cs" /> <Compile Include="Properties\_Version.cs" />
<Compile Include="Utilities\DictionaryHelper.cs" />
<Compile Include="Utilities\EnumHelper.cs" /> <Compile Include="Utilities\EnumHelper.cs" />
<Compile Include="Process\ExecutableHelper.cs" /> <Compile Include="Process\ExecutableHelper.cs" />
<Compile Include="Utilities\ApplicationConfig.cs" /> <Compile Include="Utilities\ApplicationConfig.cs" />

View File

@ -49,7 +49,7 @@ namespace com.muldersoft.slunkcrypt.gui
private volatile int m_isInitialized = 0; private volatile int m_isInitialized = 0;
private volatile ModeOfOperation m_modeOfOperation = (ModeOfOperation)(-1); 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 volatile SlunkCryptRunner m_processRunner = null;
private uint? m_menuId_disableAnimation = null, m_menuId_enableExpertMode = 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_dispatcherTimer.Interval = TimeSpan.FromMilliseconds(331);
m_logFileReadOnly = new ReadOnlyObservableCollection<string>(m_logFile); m_logFileReadOnly = new ReadOnlyObservableCollection<string>(m_logFile);
m_disableAnimation = m_config.DisableBusyIndicator; 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; Hint_SoftwareRendering.Visibility = Visibility.Visible;
} }
if (m_config.LegacyCompat) if (m_config.LegacyCompat > 1)
{ {
Checkbox_Encrypt_LegacyCompat.IsChecked = Checkbox_Decrypt_LegacyCompat.IsChecked = true; 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) private void Process_OutputAvailable(string line, bool stderr)
{ {
AppendLogFile(line); AppendLogFile(line);
if (line.IndexOf("Checksum mismatch detected!", StringComparison.OrdinalIgnoreCase) >= 0)
{
m_checksumError = true;
}
} }
private void Porcess_ProgressChanged(double progress) 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) 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; string passwordStr;
if (string.IsNullOrEmpty(passwordStr = passwordEdit.Password) || (passwordStr.Length < MIN_PASSWD_LENGTH)) if (string.IsNullOrEmpty(passwordStr = passwordEdit.Password) || (passwordStr.Length < MIN_PASSWD_LENGTH))
{ {
@ -548,7 +548,7 @@ namespace com.muldersoft.slunkcrypt.gui
return; 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) 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); ResetKeyboardFocus(this);
SetProgress(double.PositiveInfinity); SetProgress(double.PositiveInfinity);
ClearLogFile(); 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 (!await processor(inputFile, outputFile, password, enableLegacyCompat))
{ {
if (!m_config.KeepIncompleteFiles) if (!m_config.KeepIncompleteFiles)
@ -583,10 +583,10 @@ namespace com.muldersoft.slunkcrypt.gui
private async Task<bool> Encrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat) private async Task<bool> Encrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat)
{ {
SetStatus("Please wait while the encryption process is initializing..."); SetStatus("Please wait while the encryption process is initializing...");
int? exitCode = await RunProcess(SlunkCryptRunner.Mode.Encrypt, inputFile, outputFile, password, enableLegacyCompat); Tuple<int, SlunkCryptRunner.Error?> result = await RunProcess(SlunkCryptRunner.Mode.Encrypt, inputFile, outputFile, password, enableLegacyCompat);
if (exitCode.HasValue) if (!ReferenceEquals(result, null))
{ {
if (exitCode.Value == 0) if (result.Item1 == 0)
{ {
SetProgress(1); SetProgress(1);
SetStatus("Completed: The file has been encrypted successfully.", Status.Success); SetStatus("Completed: The file has been encrypted successfully.", Status.Success);
@ -594,8 +594,16 @@ namespace com.muldersoft.slunkcrypt.gui
} }
else 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); SetProgress(1, true);
SetStatus("Error: Failed to enecrypt the file. Please see the log file for details!", Status.Failure);
SystemSounds.Hand.Play(); SystemSounds.Hand.Play();
} }
return true; return true;
@ -606,10 +614,10 @@ namespace com.muldersoft.slunkcrypt.gui
private async Task<bool> Decrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat) private async Task<bool> Decrypt(string inputFile, string outputFile, string password, bool enableLegacyCompat)
{ {
SetStatus("Please wait while the decryption process is initializing..."); SetStatus("Please wait while the decryption process is initializing...");
int? exitCode = await RunProcess(SlunkCryptRunner.Mode.Decrypt, inputFile, outputFile, password, enableLegacyCompat); Tuple<int, SlunkCryptRunner.Error?> result = await RunProcess(SlunkCryptRunner.Mode.Decrypt, inputFile, outputFile, password, enableLegacyCompat);
if (exitCode.HasValue) 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); SetStatus("Completed: The file has been decrypted successfully (checksum is correct).", Status.Success);
SetProgress(1); SetProgress(1);
@ -617,13 +625,14 @@ namespace com.muldersoft.slunkcrypt.gui
} }
else else
{ {
if (m_checksumError) switch (result.Item2)
{ {
SetStatus("Error: Checksum mismatch detected! Wrong passphrase or corrupted file?", Status.Failure); case SlunkCryptRunner.Error.Checksum:
} SetStatus("Error: Checksum mismatch detected! Wrong passphrase or corrupted file?", Status.Failure);
else break;
{ default:
SetStatus("Error: Failed to decrypt the file. Please see the log file for details!", Status.Failure); SetStatus("Error: Failed to decrypt the file. Please see the log file for details!", Status.Failure);
break;
} }
SetProgress(1, true); SetProgress(1, true);
SystemSounds.Hand.Play(); SystemSounds.Hand.Play();
@ -633,7 +642,7 @@ namespace com.muldersoft.slunkcrypt.gui
return false; return false;
} }
private async Task<int?> RunProcess(SlunkCryptRunner.Mode mode, string inputFile, string outputFile, string password, bool enableLegacyCompat) private async Task<Tuple<int, SlunkCryptRunner.Error?>> RunProcess(SlunkCryptRunner.Mode mode, string inputFile, string outputFile, string password, bool enableLegacyCompat)
{ {
if (!ReferenceEquals(m_processRunner, null)) if (!ReferenceEquals(m_processRunner, null))
{ {
@ -647,7 +656,8 @@ namespace com.muldersoft.slunkcrypt.gui
m_processRunner.OutputAvailable += Process_OutputAvailable; m_processRunner.OutputAvailable += Process_OutputAvailable;
m_processRunner.ProgressChanged += Porcess_ProgressChanged; m_processRunner.ProgressChanged += Porcess_ProgressChanged;
SetProcessPriority(ProcessPriorityClass.AboveNormal); 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) catch (ProcessRunner.ProcessStartException err)

View File

@ -34,15 +34,15 @@ namespace com.muldersoft.slunkcrypt.gui.utils
{ {
get 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 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)))); 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 // Helper class
// ============================================================================= // =============================================================================
@ -116,6 +121,14 @@ namespace com.muldersoft.slunkcrypt.gui.utils
{ {
return result; return result;
} }
else
{
bool boolean;
if (bool.TryParse(value.Trim(), out boolean))
{
return Convert.ToInt32(boolean);
}
}
} }
return null; return null;
} }

View File

@ -0,0 +1,18 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* 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<K, V> ToReadOnlyDictionary<K, V>(this IDictionary<K, V> dict)
{
return (dict is ReadOnlyDictionary<K, V>) ? ((ReadOnlyDictionary<K, V>)dict) : new ReadOnlyDictionary<K, V>(dict);
}
}
}