Skip to content

Commit

Permalink
UI: Edit CipherSuite configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
Rans4ckeR committed Dec 29, 2023
1 parent b54775b commit 4c2c582
Show file tree
Hide file tree
Showing 24 changed files with 569 additions and 423 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<FileVersion>0.0.0.1</FileVersion>
<InformationalVersion>0.0.0.1</InformationalVersion>
<Platforms>AnyCPU</Platforms>
<EnableSourceControlManagerQueries Condition="$(Configuration) == 'Debug'">true</EnableSourceControlManagerQueries> <!--https://github.com/dotnet/sdk/issues/36666-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
Expand Down
1 change: 1 addition & 0 deletions CipherPunk.UI/CipherPunk.UI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<Platforms>x86;x64;ARM64</Platforms>
<ApplicationManifest>app.manifest</ApplicationManifest>
<EnableSourceControlManagerQueries Condition="$(Configuration) == 'Debug'">true</EnableSourceControlManagerQueries> <!--https://github.com/dotnet/sdk/issues/36666-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
Expand Down
1 change: 0 additions & 1 deletion CipherPunk.UI/Properties/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma warning disable IDE0065 // Misplaced using directive
#pragma warning disable SA1200 // Using directives should be placed correctly
global using CipherPunk;
global using Microsoft.Extensions.Logging;
#pragma warning restore SA1200 // Using directives should be placed correctly
#pragma warning restore IDE0065 // Misplaced using directive
9 changes: 3 additions & 6 deletions CipherPunk.UI/Services/UacIconService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public BitmapSource GetUacShieldIcon()
{
HRESULT shGetStockIconInfoResult = PInvoke.SHGetStockIconInfo(SHSTOCKICONID.SIID_SHIELD, SHGSI_FLAGS.SHGSI_ICON | SHGSI_FLAGS.SHGSI_SMALLICON, ref psii);

if (!shGetStockIconInfoResult.Succeeded)
throw Marshal.GetExceptionForHR(shGetStockIconInfoResult)!;
if (shGetStockIconInfoResult.Failed)
throw new Win32Exception(shGetStockIconInfoResult);

bitmapSource = Imaging.CreateBitmapSourceFromHIcon(psii.hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
Expand All @@ -35,9 +35,6 @@ public BitmapSource GetUacShieldIcon()
destroyIconResult = PInvoke.DestroyIcon(psii.hIcon);
}

if (destroyIconResult.Value is 0)
throw new Win32Exception(Marshal.GetLastWin32Error());

return bitmapSource;
return destroyIconResult.Value is 0 ? throw new Win32Exception(Marshal.GetLastWin32Error()) : bitmapSource;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
namespace CipherPunk.UI;

using System.Collections.ObjectModel;
using System.Windows.Media.Imaging;
using CipherPunk.CipherSuiteInfoApi;
using CommunityToolkit.Mvvm.Input;
using Windows.Win32;

internal abstract class BaseCipherSuitesSettingsViewModel : BaseViewModel
{
private readonly IUacIconService uacIconService;
private readonly ICipherSuiteInfoApiService cipherSuiteInfoApiService;
private readonly List<CipherSuite?> onlineCipherSuiteInfos = [];
private ObservableCollection<UiWindowsApiCipherSuiteConfiguration>? activeCipherSuiteConfigurations;
private ObservableCollection<UiWindowsApiCipherSuiteConfiguration>? modifiedCipherSuiteConfigurations;
private ObservableCollection<UiWindowsDocumentationCipherSuiteConfiguration>? defaultCipherSuiteConfigurations;
private BitmapSource? uacIcon;
private bool fetchOnlineInfo = true;

protected BaseCipherSuitesSettingsViewModel(
ILogger logger, ICipherSuiteService cipherSuiteService, IUacIconService uacIconService, ICipherSuiteInfoApiService cipherSuiteInfoApiService)
: base(logger)
{
MoveCipherSuiteUpCommand = new RelayCommand<UiWindowsApiCipherSuiteConfiguration?>(ExecuteMoveCipherSuiteUpCommand, CanExecuteMoveCipherSuiteUpCommand);
MoveCipherSuiteDownCommand = new RelayCommand<UiWindowsApiCipherSuiteConfiguration?>(ExecuteMoveCipherSuiteDownCommand, CanExecuteMoveCipherSuiteDownCommand);
DeleteCipherSuiteCommand = new RelayCommand<UiWindowsApiCipherSuiteConfiguration?>(ExecuteDeleteCipherSuiteCommand, CanExecuteDeleteCipherSuiteCommand);
AddCipherSuiteCommand = new RelayCommand<UiWindowsDocumentationCipherSuiteConfiguration?>(ExecuteAddCipherSuiteCommand, CanExecuteAddCipherSuiteCommand);
SaveCipherSuitesCommand = new AsyncRelayCommand(ExecuteSaveCipherSuitesCommandAsync, CanExecuteSaveCipherSuitesCommand);
CancelCipherSuitesCommand = new RelayCommand(ExecuteCancelCipherSuitesCommand, CanExecuteCancelCipherSuitesCommand);
ResetCipherSuitesCommand = new AsyncRelayCommand(ExecuteResetCipherSuitesCommandAsync, CanExecuteResetCipherSuitesCommand);
CipherSuiteService = cipherSuiteService;
this.uacIconService = uacIconService;
this.cipherSuiteInfoApiService = cipherSuiteInfoApiService;

UpdateCanExecuteDefaultCommand();
}

public IRelayCommand MoveCipherSuiteUpCommand { get; }

public IRelayCommand MoveCipherSuiteDownCommand { get; }

public IRelayCommand DeleteCipherSuiteCommand { get; }

public IRelayCommand AddCipherSuiteCommand { get; }

public IAsyncRelayCommand SaveCipherSuitesCommand { get; }

public IRelayCommand CancelCipherSuitesCommand { get; }

public IAsyncRelayCommand ResetCipherSuitesCommand { get; }

public BitmapSource UacIcon => uacIcon ??= uacIconService.GetUacShieldIcon();

public bool FetchOnlineInfo
{
get => fetchOnlineInfo;
set => _ = SetProperty(ref fetchOnlineInfo, value);
}

protected ICipherSuiteService CipherSuiteService { get; }

public ObservableCollection<UiWindowsApiCipherSuiteConfiguration>? ModifiedCipherSuiteConfigurations
{
get => modifiedCipherSuiteConfigurations;
private set => _ = SetProperty(ref modifiedCipherSuiteConfigurations, value);
}

public ObservableCollection<UiWindowsDocumentationCipherSuiteConfiguration>? DefaultCipherSuiteConfigurations
{
get => defaultCipherSuiteConfigurations;
private set => _ = SetProperty(ref defaultCipherSuiteConfigurations, value);
}

protected override async Task DoExecuteDefaultCommandAsync(CancellationToken cancellationToken)
{
IList<WindowsDocumentationCipherSuiteConfiguration> windowsDocumentationCipherSuiteConfigurations = CipherSuiteService.GetOperatingSystemDocumentationDefaultCipherSuiteList();
IEnumerable<WindowsApiCipherSuiteConfiguration> windowsApiActiveCipherSuiteConfigurations = GetActiveCipherSuiteConfiguration();

if (FetchOnlineInfo)
await FetchOnlineCipherSuiteInfoAsync(windowsDocumentationCipherSuiteConfigurations, cancellationToken);

ushort priority = ushort.MinValue;
var uiWindowsApiCipherSuiteConfigurations = windowsApiActiveCipherSuiteConfigurations.Select(q => new UiWindowsApiCipherSuiteConfiguration(
++priority,
q.CipherSuite,
q.Protocols.Contains(SslProviderProtocolId.SSL2_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.SSL3_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.TLS1_0_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.TLS1_1_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.TLS1_2_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.TLS1_3_PROTOCOL_VERSION),
q.KeyType,
q.Certificate,
q.MaximumExchangeLength,
q.MinimumExchangeLength,
q.Exchange,
q.HashLength,
q.Hash,
q.CipherBlockLength,
q.CipherLength,
q.Cipher,
onlineCipherSuiteInfos.SingleOrDefault(r => q.CipherSuite.ToString().Equals(r!.Value.IanaName, StringComparison.OrdinalIgnoreCase), null)?.Security)).ToList();

priority = ushort.MinValue;

var defaultUiWindowsDocumentationCipherSuiteConfigurations = windowsDocumentationCipherSuiteConfigurations.Select(q => new UiWindowsDocumentationCipherSuiteConfiguration(
++priority,
q.CipherSuite,
q.AllowedByUseStrongCryptographyFlag,
q.EnabledByDefault,
q.Protocols.Contains(SslProviderProtocolId.SSL2_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.SSL3_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.TLS1_0_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.TLS1_1_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.TLS1_2_PROTOCOL_VERSION),
q.Protocols.Contains(SslProviderProtocolId.TLS1_3_PROTOCOL_VERSION),
q.ExplicitApplicationRequestOnly,
q.PreWindows10EllipticCurve,
onlineCipherSuiteInfos.SingleOrDefault(r => q.CipherSuite.ToString().Equals(r!.Value.IanaName, StringComparison.OrdinalIgnoreCase), null)?.Security)).ToList();

DefaultCipherSuiteConfigurations = new(defaultUiWindowsDocumentationCipherSuiteConfigurations);
activeCipherSuiteConfigurations = new(uiWindowsApiCipherSuiteConfigurations);
ModifiedCipherSuiteConfigurations = new(activeCipherSuiteConfigurations);
}

protected abstract IEnumerable<WindowsApiCipherSuiteConfiguration> GetActiveCipherSuiteConfiguration();

protected abstract void DoExecuteSaveCipherSuitesCommand();

protected abstract void DoExecuteResetCipherSuitesCommand();

protected virtual bool CanExecuteMoveCipherSuiteUpCommand(UiWindowsApiCipherSuiteConfiguration? uiWindowsApiCipherSuiteConfiguration)
=> uiWindowsApiCipherSuiteConfiguration is not null && ModifiedCipherSuiteConfigurations!.IndexOf(uiWindowsApiCipherSuiteConfiguration.Value) > 0;

protected virtual bool CanExecuteMoveCipherSuiteDownCommand(UiWindowsApiCipherSuiteConfiguration? uiWindowsApiCipherSuiteConfiguration)
=> uiWindowsApiCipherSuiteConfiguration is not null && ModifiedCipherSuiteConfigurations!.IndexOf(uiWindowsApiCipherSuiteConfiguration.Value) < ModifiedCipherSuiteConfigurations.Count - 1;

protected virtual bool CanExecuteDeleteCipherSuiteCommand(UiWindowsApiCipherSuiteConfiguration? uiWindowsApiCipherSuiteConfiguration)
=> uiWindowsApiCipherSuiteConfiguration is not null;

protected virtual bool CanExecuteSaveCipherSuitesCommand()
=> !(activeCipherSuiteConfigurations?.SequenceEqual(ModifiedCipherSuiteConfigurations ?? []) ?? false);

protected virtual bool CanExecuteCancelCipherSuitesCommand()
=> !(activeCipherSuiteConfigurations?.SequenceEqual(ModifiedCipherSuiteConfigurations ?? []) ?? false);

protected virtual bool CanExecuteAddCipherSuiteCommand(UiWindowsDocumentationCipherSuiteConfiguration? uiWindowsDocumentationCipherSuiteConfiguration)
=> uiWindowsDocumentationCipherSuiteConfiguration is not null && ModifiedCipherSuiteConfigurations!.All(q => q.Id != uiWindowsDocumentationCipherSuiteConfiguration.Value.CipherSuite);

protected virtual bool CanExecuteResetCipherSuitesCommand()
=> true;

private async Task FetchOnlineCipherSuiteInfoAsync(IEnumerable<WindowsDocumentationCipherSuiteConfiguration> windowsDocumentationCipherSuiteConfigurations, CancellationToken cancellationToken)
{
CipherSuite?[] cipherSuites = await Task.WhenAll(windowsDocumentationCipherSuiteConfigurations.Select(q => cipherSuiteInfoApiService.GetCipherSuiteAsync(q.CipherSuite.ToString(), cancellationToken).AsTask()));

onlineCipherSuiteInfos.Clear();
onlineCipherSuiteInfos.AddRange(cipherSuites.Where(q => q is not null));
}

private void ExecuteMoveCipherSuiteUpCommand(UiWindowsApiCipherSuiteConfiguration? uiWindowsApiCipherSuiteConfiguration)
{
int index = ModifiedCipherSuiteConfigurations!.IndexOf(uiWindowsApiCipherSuiteConfiguration!.Value);

ModifiedCipherSuiteConfigurations.Move(index, --index);
NotifyCanExecuteChanged();
}

private void ExecuteMoveCipherSuiteDownCommand(UiWindowsApiCipherSuiteConfiguration? uiWindowsApiCipherSuiteConfiguration)
{
int index = ModifiedCipherSuiteConfigurations!.IndexOf(uiWindowsApiCipherSuiteConfiguration!.Value);

ModifiedCipherSuiteConfigurations.Move(index, ++index);
NotifyCanExecuteChanged();
}

private void ExecuteDeleteCipherSuiteCommand(UiWindowsApiCipherSuiteConfiguration? uiWindowsApiCipherSuiteConfiguration)
{
_ = ModifiedCipherSuiteConfigurations!.Remove(uiWindowsApiCipherSuiteConfiguration!.Value);
NotifyCanExecuteChanged();
}

private void ExecuteAddCipherSuiteCommand(UiWindowsDocumentationCipherSuiteConfiguration? uiWindowsDocumentationCipherSuiteConfiguration)
{
WindowsApiCipherSuiteConfiguration windowsApiCipherSuiteConfiguration = CipherSuiteService.GetOperatingSystemDefaultCipherSuiteList().Single(q => q.CipherSuite == uiWindowsDocumentationCipherSuiteConfiguration!.Value.CipherSuite);

ModifiedCipherSuiteConfigurations!.Add(new(
(ushort)(ModifiedCipherSuiteConfigurations.Count + 1),
windowsApiCipherSuiteConfiguration.CipherSuite,
windowsApiCipherSuiteConfiguration.Protocols.Contains(SslProviderProtocolId.SSL2_PROTOCOL_VERSION),
windowsApiCipherSuiteConfiguration.Protocols.Contains(SslProviderProtocolId.SSL3_PROTOCOL_VERSION),
windowsApiCipherSuiteConfiguration.Protocols.Contains(SslProviderProtocolId.TLS1_0_PROTOCOL_VERSION),
windowsApiCipherSuiteConfiguration.Protocols.Contains(SslProviderProtocolId.TLS1_1_PROTOCOL_VERSION),
windowsApiCipherSuiteConfiguration.Protocols.Contains(SslProviderProtocolId.TLS1_2_PROTOCOL_VERSION),
windowsApiCipherSuiteConfiguration.Protocols.Contains(SslProviderProtocolId.TLS1_3_PROTOCOL_VERSION),
windowsApiCipherSuiteConfiguration.KeyType,
windowsApiCipherSuiteConfiguration.Certificate,
windowsApiCipherSuiteConfiguration.MaximumExchangeLength,
windowsApiCipherSuiteConfiguration.MinimumExchangeLength,
windowsApiCipherSuiteConfiguration.Exchange,
windowsApiCipherSuiteConfiguration.HashLength,
windowsApiCipherSuiteConfiguration.Hash,
windowsApiCipherSuiteConfiguration.CipherBlockLength,
windowsApiCipherSuiteConfiguration.CipherLength,
windowsApiCipherSuiteConfiguration.Cipher,
onlineCipherSuiteInfos.SingleOrDefault(r => windowsApiCipherSuiteConfiguration.CipherSuite.ToString().Equals(r!.Value.IanaName, StringComparison.OrdinalIgnoreCase), null)?.Security));
NotifyCanExecuteChanged();
}

private async Task ExecuteSaveCipherSuitesCommandAsync()
{
DoExecuteSaveCipherSuitesCommand();
await DoExecuteDefaultCommandAsync(CancellationToken.None);
NotifyCanExecuteChanged();
}

private void ExecuteCancelCipherSuitesCommand()
{
ModifiedCipherSuiteConfigurations = new(activeCipherSuiteConfigurations!);

NotifyCanExecuteChanged();
}

private async Task ExecuteResetCipherSuitesCommandAsync()
{
DoExecuteResetCipherSuitesCommand();
await DoExecuteDefaultCommandAsync(CancellationToken.None);
NotifyCanExecuteChanged();
}

private void NotifyCanExecuteChanged()
{
MoveCipherSuiteUpCommand.NotifyCanExecuteChanged();
MoveCipherSuiteDownCommand.NotifyCanExecuteChanged();
SaveCipherSuitesCommand.NotifyCanExecuteChanged();
CancelCipherSuitesCommand.NotifyCanExecuteChanged();
AddCipherSuiteCommand.NotifyCanExecuteChanged();
ResetCipherSuitesCommand.NotifyCanExecuteChanged();
}
}
Loading

0 comments on commit 4c2c582

Please sign in to comment.