vault unlocking WIP

This commit is contained in:
TheXamlGuy
2024-05-03 19:33:32 +01:00
parent b17be4b0b8
commit e8b9eb7ae5
17 changed files with 102 additions and 43 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ public partial class App : Application
services.AddTemplate<ArchiveNavigationViewModel, ArchiveNavigationView>(); services.AddTemplate<ArchiveNavigationViewModel, ArchiveNavigationView>();
services.AddTemplate<VaultViewModel, VaultView>("Vault"); services.AddTemplate<VaultViewModel, VaultView>("Vault");
services.AddTemplate<LockViewModel, LockView>("Lock"); services.AddTemplate<OpenVaultViewModel, OpenVaultView>("Open");
}); });
})!); })!);
@@ -32,6 +32,9 @@
<ProjectReference Include="..\Toolkit\Toolkit.UI.Controls.Avalonia\Toolkit.UI.Controls.Avalonia.csproj" /> <ProjectReference Include="..\Toolkit\Toolkit.UI.Controls.Avalonia\Toolkit.UI.Controls.Avalonia.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="OpenVaultView.axaml.cs">
<DependentUpon>OpenVaultView.axaml</DependentUpon>
</Compile>
<Compile Update="ManageNavigationView.axaml.cs"> <Compile Update="ManageNavigationView.axaml.cs">
<DependentUpon>ManageNavigationView.axaml</DependentUpon> <DependentUpon>ManageNavigationView.axaml</DependentUpon>
</Compile> </Compile>
-8
View File
@@ -1,8 +0,0 @@
using Avalonia.Controls;
namespace Bitvault.Avalonia;
public partial class LockView : UserControl
{
public LockView() => InitializeComponent();
}
+4
View File
@@ -8,6 +8,10 @@
FooterMenuItemsSource="{Binding Footer}" FooterMenuItemsSource="{Binding Footer}"
MenuItemTemplate="{Binding Template}" MenuItemTemplate="{Binding Template}"
MenuItemsSource="{Binding}"> MenuItemsSource="{Binding}">
<NavigationView.Resources>
<CornerRadius x:Key="NavigationViewContentGridCornerRadius">0</CornerRadius>
<Thickness x:Key="NavigationViewContentGridBorderThickness">1,0,0,0</Thickness>
</NavigationView.Resources>
<Frame x:Name="Main"> <Frame x:Name="Main">
<Interaction.Behaviors> <Interaction.Behaviors>
<EventTriggerBehavior EventName="Loaded"> <EventTriggerBehavior EventName="Loaded">
@@ -1,9 +1,9 @@
<UserControl <UserControl
x:Class="Bitvault.Avalonia.LockView" x:Class="Bitvault.Avalonia.OpenVaultView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Bitvault" xmlns:vm="using:Bitvault"
x:DataType="vm:LockViewModel"> x:DataType="vm:OpenVaultViewModel">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox <TextBox
Width="300" Width="300"
@@ -12,7 +12,7 @@
Text="{Binding Password}"> Text="{Binding Password}">
<Interaction.Behaviors> <Interaction.Behaviors>
<KeyBindingTriggerBehavior Gesture="Enter"> <KeyBindingTriggerBehavior Gesture="Enter">
<InvokeCommandAction Command="{Binding UnlockCommand}" /> <InvokeCommandAction Command="{Binding InvokeCommand}" />
</KeyBindingTriggerBehavior> </KeyBindingTriggerBehavior>
</Interaction.Behaviors> </Interaction.Behaviors>
</TextBox> </TextBox>
+8
View File
@@ -0,0 +1,8 @@
using Avalonia.Controls;
namespace Bitvault.Avalonia;
public partial class OpenVaultView : UserControl
{
public OpenVaultView() => InitializeComponent();
}
+30 -1
View File
@@ -5,10 +5,39 @@
xmlns:vm="using:Bitvault" xmlns:vm="using:Bitvault"
x:DataType="vm:VaultNavigationViewModel" x:DataType="vm:VaultNavigationViewModel"
Content="{Binding Name}" Content="{Binding Name}"
IsExpanded="{Binding Expanded, Mode=TwoWay}"
IsSelected="{Binding Selected}"
MenuItemsSource="{Binding}"> MenuItemsSource="{Binding}">
<Interaction.Behaviors> <Interaction.Behaviors>
<DataTriggerBehavior Binding="{Binding Opened}" Value="False">
<ConditionAction>
<ConditionAction.Condition>
<ConditionalExpression ForwardChaining="And">
<ComparisonCondition LeftOperand="{Binding Selected}" RightOperand="True" />
</ConditionalExpression>
</ConditionAction.Condition>
<NavigateAction Context="Main" Route="Open" />
</ConditionAction>
</DataTriggerBehavior>
<DataTriggerBehavior Binding="{Binding Opened}" Value="True">
<ConditionAction>
<ConditionAction.Condition>
<ConditionalExpression ForwardChaining="And">
<ComparisonCondition LeftOperand="{Binding Selected}" RightOperand="True" />
</ConditionalExpression>
</ConditionAction.Condition>
<NavigateAction Context="Main" Route="Vault" />
</ConditionAction>
</DataTriggerBehavior>
<EventTriggerBehavior EventName="Tapped"> <EventTriggerBehavior EventName="Tapped">
<NavigateAction Context="Main" Route="Lock" /> <ConditionAction>
<ConditionAction.Condition>
<ConditionalExpression ForwardChaining="And">
<ComparisonCondition LeftOperand="{Binding Opened}" RightOperand="False" />
</ConditionalExpression>
</ConditionAction.Condition>
<NavigateAction Context="Main" Route="Open" />
</ConditionAction>
</EventTriggerBehavior> </EventTriggerBehavior>
</Interaction.Behaviors> </Interaction.Behaviors>
</NavigationViewItem> </NavigationViewItem>
+1 -1
View File
@@ -1,3 +1,3 @@
namespace Bitvault; namespace Bitvault;
public record Locked; public record Closed;
+4 -2
View File
@@ -20,10 +20,11 @@ public class CreateVaultHandler(IVaultComponentFactory componentFactory) :
IContainer<VaultKey> vaultKeyContainer = host.Services.GetRequiredService<IContainer<VaultKey>>(); IContainer<VaultKey> vaultKeyContainer = host.Services.GetRequiredService<IContainer<VaultKey>>();
IVaultStorage vaultStorage = host.Services.GetRequiredService<IVaultStorage>(); IVaultStorage vaultStorage = host.Services.GetRequiredService<IVaultStorage>();
VaultKey key = keyVaultFactory.Create(Encoding.UTF8.GetBytes(password)); if (keyVaultFactory.Create(Encoding.UTF8.GetBytes(password)) is VaultKey key)
{
vaultKeyContainer.Set(key); vaultKeyContainer.Set(key);
if (await vaultStorage.CreateAsync(name, key)) if (await vaultStorage.Create(name, key))
{ {
IWritableConfiguration<VaultConfiguration> configuration = IWritableConfiguration<VaultConfiguration> configuration =
host.Services.GetRequiredService<IWritableConfiguration<VaultConfiguration>>(); host.Services.GetRequiredService<IWritableConfiguration<VaultConfiguration>>();
@@ -35,6 +36,7 @@ public class CreateVaultHandler(IVaultComponentFactory componentFactory) :
} }
} }
} }
}
return false; return false;
} }
+1 -1
View File
@@ -2,7 +2,7 @@
public interface IVaultKeyFactory public interface IVaultKeyFactory
{ {
VaultKey Create(byte[] phrase, VaultKey? Create(byte[] phrase,
byte[]? encryptedKey = null, byte[]? encryptedKey = null,
byte[]? salt = null); byte[]? salt = null);
} }
+1 -1
View File
@@ -2,5 +2,5 @@
public interface IVaultStorage public interface IVaultStorage
{ {
Task<bool> CreateAsync(string name, VaultKey key); Task<bool> Create(string name, VaultKey key);
} }
+5 -3
View File
@@ -18,10 +18,12 @@ public class OpenVaultHandler(VaultConfiguration configuration,
byte[]? salt = Convert.FromBase64String(keyPart[0]); byte[]? salt = Convert.FromBase64String(keyPart[0]);
byte[]? encryptedKey = Convert.FromBase64String(keyPart[1]); byte[]? encryptedKey = Convert.FromBase64String(keyPart[1]);
VaultKey key = keyVaultFactory.Create(Encoding.UTF8.GetBytes(password), encryptedKey, salt); if ( keyVaultFactory.Create(Encoding.UTF8.GetBytes(password), encryptedKey, salt) is VaultKey key)
if (await vaultStorage.CreateAsync(name, key))
{ {
if (await vaultStorage.Create(name, key))
{
return true;
}
} }
} }
} }
@@ -4,7 +4,7 @@ using Toolkit.Foundation;
namespace Bitvault; namespace Bitvault;
public partial class LockViewModel(IServiceProvider provider, public partial class OpenVaultViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
IPublisher publisher, IPublisher publisher,
@@ -16,11 +16,14 @@ public partial class LockViewModel(IServiceProvider provider,
private string? password; private string? password;
[RelayCommand] [RelayCommand]
private void Unlock() private async Task Invoke()
{ {
if (Password is { Length: > 0 }) if (Password is { Length: > 0 })
{ {
Mediator.Handle<Open<Vault>, bool>(Open.As(new Vault(Password))); if (await Mediator.Handle<Open<Vault>, bool>(Open.As(new Vault(Password))))
{
await Publisher.Publish<Opened>();
}
} }
} }
} }
+1 -1
View File
@@ -1,3 +1,3 @@
namespace Bitvault; namespace Bitvault;
public record Unlocked; public record Opened;
+13 -3
View File
@@ -8,15 +8,25 @@ public class VaultKeyFactory(IKeyGenerator generator,
IDecryptor decryptor) : IDecryptor decryptor) :
IVaultKeyFactory IVaultKeyFactory
{ {
public VaultKey Create(byte[] phrase, public VaultKey? Create(byte[] phrase,
byte[]? encryptedKey = null, byte[]? encryptedKey = null,
byte[]? salt = null) byte[]? salt = null)
{ {
salt ??= generator.Generate(16); salt ??= generator.Generate(16);
byte[] derivedKey = deriver.DeriveKey(phrase, salt); byte[] derivedKey = deriver.DeriveKey(phrase, salt);
encryptedKey ??= encryptor.Encrypt(generator.Generate(32), derivedKey); if (encryptedKey is null)
byte[] decryptedKey = decryptor.Decrypt(encryptedKey, derivedKey); {
if (!encryptor.TryEncrypt(generator.Generate(32), derivedKey, out encryptedKey) || encryptedKey is null)
{
return default;
}
}
if (!decryptor.TryDecrypt(encryptedKey, derivedKey, out byte[]? decryptedKey) || decryptedKey is null)
{
return default;
}
return new VaultKey(salt, encryptedKey, decryptedKey); return new VaultKey(salt, encryptedKey, decryptedKey);
} }
+13 -7
View File
@@ -6,15 +6,21 @@ namespace Bitvault;
public partial class VaultNavigationViewModel : public partial class VaultNavigationViewModel :
ObservableCollectionViewModel<IVaultNavigationViewModel>, ObservableCollectionViewModel<IVaultNavigationViewModel>,
IMainNavigationViewModel, IMainNavigationViewModel,
INotificationHandler<Unlocked>, INotificationHandler<Opened>,
INotificationHandler<Locked> INotificationHandler<Closed>
{ {
[ObservableProperty] [ObservableProperty]
private bool locked; private bool expanded = true;
[ObservableProperty] [ObservableProperty]
private string name; private string name;
[ObservableProperty]
private bool opened;
[ObservableProperty]
private bool selected;
public VaultNavigationViewModel(IServiceProvider provider, public VaultNavigationViewModel(IServiceProvider provider,
IServiceFactory factory, IServiceFactory factory,
IMediator mediator, IMediator mediator,
@@ -30,9 +36,9 @@ public partial class VaultNavigationViewModel :
public IContentTemplate Template { get; set; } public IContentTemplate Template { get; set; }
public Task Handle(Unlocked args, CancellationToken cancellationToken = default) public Task Handle(Opened args, CancellationToken cancellationToken = default)
{ {
Locked = true; Opened = true;
Add<AllNavigationViewModel>(); Add<AllNavigationViewModel>();
Add<StarredNavigationViewModel>(); Add<StarredNavigationViewModel>();
@@ -42,9 +48,9 @@ public partial class VaultNavigationViewModel :
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task Handle(Locked args, CancellationToken cancellationToken = default) public Task Handle(Closed args, CancellationToken cancellationToken = default)
{ {
Locked = true; Opened = true;
Clear(); Clear();
return Task.CompletedTask; return Task.CompletedTask;
+1 -1
View File
@@ -10,7 +10,7 @@ public class VaultStorage(IContainer<VaultStorageConnection> connection,
IServiceProvider provider) : IServiceProvider provider) :
IVaultStorage IVaultStorage
{ {
public async Task<bool> CreateAsync(string name, public async Task<bool> Create(string name,
VaultKey key) VaultKey key)
{ {
connection.Set(new VaultStorageConnection($"Data Source={Path.Combine(environment.ContentRootPath, name)}" + connection.Set(new VaultStorageConnection($"Data Source={Path.Combine(environment.ContentRootPath, name)}" +