vault unlocking WIP
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
|
|
||||||
namespace Bitvault.Avalonia;
|
|
||||||
|
|
||||||
public partial class LockView : UserControl
|
|
||||||
{
|
|
||||||
public LockView() => InitializeComponent();
|
|
||||||
}
|
|
||||||
@@ -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>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Bitvault.Avalonia;
|
||||||
|
|
||||||
|
public partial class OpenVaultView : UserControl
|
||||||
|
{
|
||||||
|
public OpenVaultView() => InitializeComponent();
|
||||||
|
}
|
||||||
@@ -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,3 +1,3 @@
|
|||||||
namespace Bitvault;
|
namespace Bitvault;
|
||||||
|
|
||||||
public record Locked;
|
public record Closed;
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
public interface IVaultStorage
|
public interface IVaultStorage
|
||||||
{
|
{
|
||||||
Task<bool> CreateAsync(string name, VaultKey key);
|
Task<bool> Create(string name, VaultKey key);
|
||||||
}
|
}
|
||||||
@@ -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,3 +1,3 @@
|
|||||||
namespace Bitvault;
|
namespace Bitvault;
|
||||||
|
|
||||||
public record Unlocked;
|
public record Opened;
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)}" +
|
||||||
|
|||||||
Reference in New Issue
Block a user