GUI: Replaced the built-in password generator with call to SlunkCrypt CLI program + bump target framework version to 4.5.2.

This commit is contained in:
LoRd_MuldeR 2022-02-09 00:44:56 +01:00
parent b39b7cafaa
commit 5dfaabf6c4
Signed by: mulder
GPG Key ID: 2B5913365F57E03F
9 changed files with 257 additions and 257 deletions

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
<appSettings>
<add key="DisableBusyIndicator" value="false" />
<add key="DisableBusyIndicator" value="false"/>
</appSettings>
</configuration>
</configuration>

View File

@ -0,0 +1,79 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
using System;
using System.Diagnostics;
using System.IO;
using com.muldersoft.slunkcrypt.gui.utils;
namespace com.muldersoft.slunkcrypt.gui.process
{
static class ExecutableHelper
{
private const string FILENAME_FORMAT = "slunkcrypt-cli-{0}.exe";
// =============================================================================
// Exception classes
// =============================================================================
public class ExecutableNotFoundException : FileNotFoundException
{
public ExecutableNotFoundException(string message, string fileName) : base(message, fileName)
{
}
}
// =============================================================================
// Public methods
// =============================================================================
public static FileStream GetExecutableFile()
{
FileStream executableFile = null;
string appBaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
CPUFeatures cpuFeatures = CPUFeatures.Features;
if (cpuFeatures.x64 && CheckExecutableFile(ref executableFile, appBaseDirectory, "x64"))
{
Trace.Assert(executableFile != null);
return executableFile;
}
if (cpuFeatures.sse2 && CheckExecutableFile(ref executableFile, appBaseDirectory, "sse2"))
{
Trace.Assert(executableFile != null);
return executableFile;
}
if (CheckExecutableFile(ref executableFile, appBaseDirectory, "i686"))
{
Trace.Assert(executableFile != null);
return executableFile;
}
throw new ExecutableNotFoundException("SlunkCrypt executable file not found!", FILENAME_FORMAT);
}
// =============================================================================
// Internal methods
// =============================================================================
private static bool CheckExecutableFile(ref FileStream executableFile, string appBaseDirectory, string suffix)
{
try
{
executableFile = new FileStream(Path.Combine(appBaseDirectory, String.Format(FILENAME_FORMAT, suffix)), FileMode.Open, FileAccess.Read, FileShare.Read);
Version appVersion = VersionInfo.Version;
FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(executableFile.Name);
if (string.Equals(fileVersion.FileDescription, "SlunkCrypt", StringComparison.OrdinalIgnoreCase) &&
string.Equals(fileVersion.CompanyName, "Muldersoft", StringComparison.OrdinalIgnoreCase) &&
(fileVersion.FileMajorPart == appVersion.Major) && (fileVersion.FileMinorPart == appVersion.Minor))
{
return true;
}
executableFile.Dispose(); /*clean-up*/
}
catch { }
return false;
}
}
}

139
gui/Process/PasswordGen.cs Normal file
View File

@ -0,0 +1,139 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace com.muldersoft.slunkcrypt.gui.process
{
public static class PasswordGen
{
private static readonly TimeSpan TIMEOUT_MSEC = TimeSpan.FromSeconds(90);
private const string COMMAND_PASSWRD = "-p {0:D}";
// =============================================================================
// Exception classes
// =============================================================================
public class GenerationFailedException : IOException
{
public GenerationFailedException(string message, Exception innerException) : base(message, innerException)
{
}
}
// =============================================================================
// Public methods
// =============================================================================
public static async Task<string> GeneratePassword(int length)
{
using (FileStream executableFile = ExecutableHelper.GetExecutableFile())
{
try
{
string password = await StartProcess(executableFile, length);
if (IsWeakPassword(password))
{
throw new InvalidDataException("The generated password string is invalid!");
}
return password;
}
catch (Exception e)
{
throw new GenerationFailedException("Failed to generate password string!", e);
}
}
}
public static bool IsWeakPassword(string password)
{
int flags = 0;
if (!string.IsNullOrEmpty(password))
{
foreach (char c in password)
{
if (char.IsLetter(c))
{
flags |= char.IsUpper(c) ? 0x2 : 0x1;
}
else
{
flags |= char.IsDigit(c) ? 0x8 : 0x4;
}
}
}
return (flags != 0xF);
}
// =============================================================================
// Internal methods
// =============================================================================
private static async Task<string> StartProcess(FileStream executableFile, int length)
{
using (Process process = new Process())
{
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
process.StartInfo.FileName = executableFile.Name;
process.StartInfo.Arguments = string.Format(COMMAND_PASSWRD, length);
process.Start();
Stack<string> outputLines = await WaitForExit(process, TIMEOUT_MSEC);
if (process.ExitCode == 0)
{
while (outputLines.Count > 0)
{
string line = outputLines.Pop();
if (line.Length >= length)
{
return line;
}
}
}
return string.Empty;
}
}
private static async Task<Stack<string>> WaitForExit(Process process, TimeSpan timeout)
{
Task<Stack<string>> readTask;
await Task.WhenAny(readTask = Task.Run(() => ReadOutput(process)), Task.Delay(timeout));
if (!process.WaitForExit(125))
{
process.Kill();
process.WaitForExit();
}
return await readTask;
}
private static Stack<string> ReadOutput(Process process)
{
Stack<string> result = new Stack<string>();
using (StreamReader reader = process.StandardOutput)
{
string line;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length != 0)
{
result.Push(line.Trim());
}
}
}
return result;
}
}
}

View File

@ -20,7 +20,6 @@ namespace com.muldersoft.slunkcrypt.gui.process
{
public enum Mode { Encrypt, Decrypt }
private const string FILENAME_FORMAT = "slunkcrypt-cli-{0}.exe";
private const string COMMAND_ENCRYPT = "-e";
private const string COMMAND_DECRYPT = "-d";
@ -28,24 +27,13 @@ namespace com.muldersoft.slunkcrypt.gui.process
private readonly FileStream m_executableFile;
// =============================================================================
// Exception classes
// =============================================================================
public class ExecutableNotFoundException : FileNotFoundException
{
public ExecutableNotFoundException(string message, string fileName) : base(message, fileName)
{
}
}
// =============================================================================
// Constructor
// =============================================================================
public SlunkCryptRunner(Dispatcher dispatcher, ProcessPriorityClass? priorityClass = null) : base(dispatcher, priorityClass)
{
m_executableFile = GetExecutableFile();
m_executableFile = ExecutableHelper.GetExecutableFile();
}
// =============================================================================
@ -74,48 +62,6 @@ namespace com.muldersoft.slunkcrypt.gui.process
// Internal methods
// =============================================================================
private static FileStream GetExecutableFile()
{
FileStream executableFile = null;
string appBaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
CPUFeatures cpuFeatures = CPUFeatures.Features;
if (cpuFeatures.x64 && CheckExecutableFile(ref executableFile, appBaseDirectory, "x64"))
{
Trace.Assert(executableFile != null);
return executableFile;
}
if (cpuFeatures.sse2 && CheckExecutableFile(ref executableFile, appBaseDirectory, "sse2"))
{
Trace.Assert(executableFile != null);
return executableFile;
}
if (CheckExecutableFile(ref executableFile, appBaseDirectory, "i686"))
{
Trace.Assert(executableFile != null);
return executableFile;
}
throw new ExecutableNotFoundException("SlunkCrypt executable file not found!", FILENAME_FORMAT);
}
private static bool CheckExecutableFile(ref FileStream executableFile, string appBaseDirectory, string suffix)
{
try
{
executableFile = new FileStream(Path.Combine(appBaseDirectory, String.Format(FILENAME_FORMAT, suffix)), FileMode.Open, FileAccess.Read, FileShare.Read);
Version appVersion = VersionInfo.Version;
FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(executableFile.Name);
if (string.Equals(fileVersion.FileDescription, "SlunkCrypt", StringComparison.OrdinalIgnoreCase) &&
string.Equals(fileVersion.CompanyName, "Muldersoft", StringComparison.OrdinalIgnoreCase) &&
(fileVersion.FileMajorPart == appVersion.Major) && (fileVersion.FileMinorPart == appVersion.Minor))
{
return true;
}
executableFile.Dispose(); /*clean-up*/
}
catch { }
return false;
}
protected override double ParseProgressString(StringBuilder currentLine)
{
Match match;

View File

@ -12,7 +12,7 @@ namespace com.muldersoft.slunkcrypt.gui.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

View File

@ -9,10 +9,11 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>com.muldersoft.slunkcrypt.gui</RootNamespace>
<AssemblyName>slunkcrypt-gui</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -80,13 +81,14 @@
<Compile Include="Controls\PasswordToggleBox.xaml.cs">
<DependentUpon>PasswordToggleBox.xaml</DependentUpon>
</Compile>
<Compile Include="Process\ExecutableHelper.cs" />
<Compile Include="Utilities\ApplicationConfig.cs" />
<Compile Include="Utilities\CPUFeatures.cs" />
<Compile Include="Process\SlunkCryptRunner.cs" />
<Compile Include="Utilities\BusyManager.cs" />
<Compile Include="Utilities\CPU\CPUCapabilities.cs" />
<Compile Include="Process\PasswordGen.cs" />
<Compile Include="Utilities\PathUtils.cs" />
<Compile Include="Utilities\SecureRandom.cs" />
<Compile Include="Utilities\SystemMenu.cs" />
<Compile Include="Utilities\VisibilityConverter.cs" />
<Compile Include="Utilities\FontSizeConverter.cs" />

View File

@ -50,7 +50,7 @@ namespace com.muldersoft.slunkcrypt.gui
private volatile SlunkCryptRunner m_processRunner = null;
private uint? m_menuId_disableAnimation = null, m_menuId_enableExpertMode = null;
private const string ASCII_CHARS = "!#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~";
public const string ASCII_CHARS = "!#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~";
// =============================================================================
// Constructor
@ -159,10 +159,12 @@ namespace com.muldersoft.slunkcrypt.gui
using (BusyManager busy = new BusyManager(this))
{
Edit_Encrypt_Password.Password = string.Empty;
Task<string> password = Task.Run(() => GenerateRandomString(GEN_PASSWD_LENGTH));
await Task.WhenAll(password, Task.Delay(500));
Button_Encrypt_Toggle.IsChecked = true;
Edit_Encrypt_Password.Password = password.Result;
string password;
if (!string.IsNullOrEmpty(password = await GeneratePassword()))
{
Button_Encrypt_Toggle.IsChecked = true;
Edit_Encrypt_Password.Password = password;
}
}
}
@ -532,7 +534,7 @@ namespace com.muldersoft.slunkcrypt.gui
return;
}
}
else if (IsWeakPassword(passwordStr))
else if (PasswordGen.IsWeakPassword(passwordStr))
{
if (MessageBox.Show(this, "Passphrase should contain a mix of upper case characters, lower case characters, digits and other characters!", "Weak Passphrase", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) != MessageBoxResult.OK)
{
@ -635,7 +637,7 @@ namespace com.muldersoft.slunkcrypt.gui
SetStatus(string.Format("Error: The {0} process could not be created! (Error code: {1:D})", GetModeString(m_modeOfOperation), GetWin32ErrorCode(err)), Status.Failure);
MessageBox.Show(this, "Failed to create SlunkCrypt process:\n\n" + err.InnerException?.Message ?? err.Message, "Process Creation Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (SlunkCryptRunner.ExecutableNotFoundException)
catch (ExecutableHelper.ExecutableNotFoundException)
{
SetStatus("Error: The required SlunkCrypt executable file could not be found!", Status.Failure);
MessageBox.Show(this, "The SlunkCrypt executable file could not be found.\n\nPlease make sure that the SlunkCrypt CLI executable file is located in the same directory as the GUI program!", "Executable Not Found", MessageBoxButton.OK, MessageBoxImage.Error);
@ -652,6 +654,26 @@ namespace com.muldersoft.slunkcrypt.gui
return null;
}
private async Task<string> GeneratePassword()
{
string password = string.Empty;
try
{
Task<string> passwordTask = Task.Run(() => PasswordGen.GeneratePassword(GEN_PASSWD_LENGTH));
await Task.WhenAll(passwordTask, Task.Delay(1000));
password = passwordTask.Result;
}
catch (ExecutableHelper.ExecutableNotFoundException)
{
MessageBox.Show(this, "The SlunkCrypt executable file could not be found.\n\nPlease make sure that the SlunkCrypt CLI executable file is located in the same directory as the GUI program!", "Executable Not Found", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (PasswordGen.GenerationFailedException)
{
MessageBox.Show(this, "Error: The password could not be generated!", "Generation Failed", MessageBoxButton.OK, MessageBoxImage.Error);
}
return password;
}
private void SetStatus(string text, Status status = Status.Default)
{
switch (status)
@ -891,47 +913,6 @@ namespace com.muldersoft.slunkcrypt.gui
}
}
private static bool IsWeakPassword(string password)
{
int flags = 0;
foreach (char c in password)
{
if (char.IsLetter(c))
{
flags |= char.IsUpper(c) ? 0x2 : 0x1;
}
else
{
flags |= char.IsDigit(c) ? 0x8 : 0x4;
}
}
return (flags != 0xF);
}
private static string GenerateRandomString(int length)
{
if (length > 0)
{
char[] asciiChars = ASCII_CHARS.ToCharArray();
using (SecureRandom secureRandom = new SecureRandom())
{
string result;
do
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; ++i)
{
builder.Append(asciiChars[secureRandom.NextUInt32() % ((uint)asciiChars.Length)]);
}
result = builder.ToString();
}
while ((!char.IsLetterOrDigit(result[0])) || (!char.IsLetterOrDigit(result[result.Length - 1])) || IsWeakPassword(result));
return result;
}
}
return string.Empty;
}
private static void SetFocusAndSelectAll(FrameworkElement element)
{
TextBox textBox;

View File

@ -209,6 +209,5 @@ namespace com.muldersoft.slunkcrypt.gui.utils
{
return ((c == WILDCARD_ONE) || (c == WILDCARD_ANY));
}
}
}

View File

@ -1,146 +0,0 @@
/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
using System;
using System.IO;
using System.Security.Cryptography;
namespace com.muldersoft.slunkcrypt.gui.utils
{
class SecureRandom : IDisposable
{
private volatile bool m_disposed = false;
private readonly RNGCryptoServiceProvider m_provider;
private readonly byte[] m_tempBuffer = new byte[256];
private int m_offset = int.MaxValue;
// =============================================================================
// Constructor
// =============================================================================
public SecureRandom()
{
try
{
m_provider = new RNGCryptoServiceProvider();
}
catch (Exception e)
{
throw new IOException("Failed to create RNGCryptoServiceProvider instance!", e);
}
}
~SecureRandom()
{
Dispose();
}
// =============================================================================
// Public methods
// =============================================================================
public int NextInt32()
{
if (m_disposed)
{
throw new ObjectDisposedException("SecureRandom");
}
EnsureBytesAvailable(sizeof(int));
int value = BitConverter.ToInt32(m_tempBuffer, m_offset);
m_offset += sizeof(int);
return value;
}
public uint NextUInt32()
{
if (m_disposed)
{
throw new ObjectDisposedException("SecureRandom");
}
EnsureBytesAvailable(sizeof(uint));
uint value = BitConverter.ToUInt32(m_tempBuffer, m_offset);
m_offset += sizeof(uint);
return value;
}
public long NextInt64()
{
if (m_disposed)
{
throw new ObjectDisposedException("SecureRandom");
}
EnsureBytesAvailable(sizeof(long));
long value = BitConverter.ToInt64(m_tempBuffer, m_offset);
m_offset += sizeof(long);
return value;
}
public ulong NextUInt64()
{
if (m_disposed)
{
throw new ObjectDisposedException("SecureRandom");
}
EnsureBytesAvailable(sizeof(ulong));
ulong value = BitConverter.ToUInt64(m_tempBuffer, m_offset);
m_offset += sizeof(ulong);
return value;
}
public void Dispose()
{
if (!m_disposed)
{
m_disposed = true;
GC.SuppressFinalize(this);
try
{
FillArray(m_tempBuffer, 0);
}
finally
{
try
{
m_provider.Dispose();
}
catch { }
}
}
}
// =============================================================================
// Internal methods
// =============================================================================
private void EnsureBytesAvailable(int length)
{
if ((m_offset >= m_tempBuffer.Length) || ((m_tempBuffer.Length - m_offset) < length))
{
try
{
m_provider.GetBytes(m_tempBuffer);
m_offset = 0;
}
catch (Exception e)
{
throw new IOException("Failed to generated random bytes!", e);
}
}
}
private static void FillArray(byte[] array, byte value)
{
if (!ReferenceEquals(array, null))
{
for (int i = 0; i < array.Length; ++i)
{
array[i] = value;
}
}
}
}
}