This commit is contained in:
TheXamlGuy
2024-04-30 20:46:47 +01:00
parent 2a4194ee22
commit 81f266d8c4
22 changed files with 296 additions and 162 deletions
+8 -6
View File
@@ -54,12 +54,17 @@ public partial class App : Application
{
args.AddServices(services =>
{
services.AddTransient<IKeyGenerator, KeyGenerator>();
services.AddTransient<IEncryptor, AesEncryptor>();
services.AddTransient<IDecryptor, AesDecryptor>();
services.AddTransient<IPasswordHasher, PasswordHasher>();
services.AddTransient<IKeyDeriver, KeyDeriver>();
services.AddTransient<IVaultFactory, VaultFactory>();
services.AddTransient<IVaultKeyGenerator, VaultKeyGenerator>();
services.AddTransient<IVaultStorage, VaultStorage>();
services.AddDbContextFactory<VaultDbContext>(args =>
{
args.UseSqlite();
@@ -69,9 +74,6 @@ public partial class App : Application
services.AddHandler<OpenVaultHandler>();
services.AddHandler<CreateVaultStorageHandler>();
services.AddHandler<OpenVaultStorageHandler>();
services.AddTemplate<VaultNavigationViewModel, VaultNavigationView>();
services.AddTemplate<AllNavigationViewModel, AllNavigationView>();
services.AddTemplate<StarredNavigationViewModel, StarredNavigationView>();
@@ -83,13 +85,13 @@ public partial class App : Application
});
})!);
services.AddSingleton<IVaultHostCollection, VaultHostCollection>();
services.AddTransient<IVaultComponentFactory, VaultComponentFactory>();
services.AddHandler<CreateVaultHandler>();
services.AddSingleton<IVaultHostCollection, VaultHostCollection>();
services.AddInitializer<VaultCollectionInitializer>();
services.AddTemplate<MainViewModel, MainView>("Main");
services.AddHandler<VaultNavigationViewModelHandler>();
services.AddTransient<FooterViewModel>();
+13 -14
View File
@@ -7,23 +7,22 @@ public class AesDecryptor :
{
private const int IvSize = 16;
public string Decrypt(string cipherText, byte[] key)
public byte[] Decrypt(byte[] cipher, byte[] key)
{
byte[] cipherData = Convert.FromBase64String(cipherText);
Span<byte> iv = cipher.AsSpan(0, IvSize);
ReadOnlySpan<byte> encryptedContent = cipher.AsSpan(IvSize);
byte[] iv = new byte[IvSize];
Array.Copy(cipherData, 0, iv, 0, IvSize); // Extract the IV from the start of the cipher data
using var aes = Aes.Create();
using Aes aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
aes.IV = iv.ToArray();
using var memoryStream = new MemoryStream(cipherData, IvSize, cipherData.Length - IvSize);
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var streamReader = new StreamReader(cryptoStream))
{
return streamReader.ReadToEnd(); // Return the decrypted text
}
using MemoryStream memoryStream = new(encryptedContent.ToArray());
using ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read);
using MemoryStream resultStream = new();
cryptoStream.CopyTo(resultStream);
return resultStream.ToArray();
}
}
+19 -14
View File
@@ -2,28 +2,33 @@
namespace Bitvault;
public class AesEncryptor : IEncryptor
public class AesEncryptor :
IEncryptor
{
public string Encrypt(string plainText, byte[] key)
{
const int IvSize = 16;
private const int IvSize = 16;
using var aes = Aes.Create();
public byte[] Encrypt(byte[] data,
byte[] key)
{
if (key.Length != 32)
{
throw new ArgumentException("Key must be 256 bits (32 bytes).");
}
using Aes aes = Aes.Create();
aes.Key = key;
aes.GenerateIV();
byte[] iv = aes.IV;
using MemoryStream memoryStream = new();
memoryStream.Write(aes.IV.AsSpan(0, IvSize));
using var memoryStream = new MemoryStream();
memoryStream.Write(iv, 0, IvSize); // Store IV at the start of the stream
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
using (var streamWriter = new StreamWriter(cryptoStream))
using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write))
{
streamWriter.Write(plainText);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
}
return Convert.ToBase64String(memoryStream.ToArray()); // Return the encrypted data in base64
return memoryStream.ToArray();
}
}
+9 -16
View File
@@ -3,30 +3,23 @@ using Toolkit.Foundation;
namespace Bitvault;
public class CreateVaultHandler(IComponentFactory componentFactory) :
public class CreateVaultHandler(IVaultComponentFactory vaultComponentFactory) :
IHandler<Create<Vault>, bool>
{
public async Task<bool> Handle(Create<Vault> args,
public Task<bool> Handle(Create<Vault> args,
CancellationToken cancellationToken)
{
if (args.Value is Vault vault)
if (args.Value is Vault vault && vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password)
{
if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password)
if (vaultComponentFactory.Create(name) is IComponentHost host)
{
if (componentFactory.Create<IVaultComponent, VaultConfiguration>($"Vault:{name}", new VaultConfiguration { Name = name }) is IComponentHost host)
{
if (host.Services.GetRequiredService<IMediator>() is IMediator mediator)
{
if (await mediator.Handle<Create<VaultStorage>, bool>(Create.As(new VaultStorage(name, password)), cancellationToken))
{
await host.StartAsync(cancellationToken);
return true;
}
}
}
IVaultFactory factory = host.Services.GetRequiredService<IVaultFactory>();
factory.Create(name, password);
return Task.FromResult(true);
}
}
return false;
return Task.FromResult(false);
}
}
+75 -59
View File
@@ -1,73 +1,89 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using System.Security.Cryptography;
using Toolkit.Foundation;
//using Microsoft.EntityFrameworkCore;
//using Microsoft.Extensions.Hosting;
//using Toolkit.Foundation;
namespace Bitvault;
//namespace Bitvault;
public class CreateVaultStorageHandler(IHostEnvironment environment,
IKeyDeriver deriver,
IEncryptor encryptor,
IDecryptor decryptor,
IDbContextFactory<VaultDbContext> dbContextFactory) : IHandler<Create<VaultStorage>, bool>
{
public async Task<bool> Handle(Create<VaultStorage> args, CancellationToken cancellationToken)
{
if (args.Value is VaultStorage vault)
{
if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password)
{
byte[] salt = new byte[16];
RandomNumberGenerator.Fill(salt);
//public class CreateVaultStorageHandler(IHostEnvironment environment,
// IKeyGenerator generator,
// IKeyDeriver deriver,
// IEncryptor encryptor,
// IDecryptor decryptor,
// IDbContextFactory<VaultDbContext> dbContextFactory,
// IWritableConfiguration<VaultConfiguration> writer) : IHandler<Create<VaultStorage>, bool>
//{
// public async Task<bool> Handle(Create<VaultStorage> args, CancellationToken cancellationToken)
// {
// if (args.Value is VaultStorage vault)
// {
// if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password)
// {
// byte[] salt = generator.Generate(16);
// byte[] key = generator.Generate(32);
byte[] key = new byte[32];
RandomNumberGenerator.Fill(key);
// byte[] derivedKey = deriver.DeriveKey(password, salt);
byte[] derivedKey = deriver.DeriveKey(password, salt);
string? encryptedKey = encryptor.Encrypt(Convert.ToBase64String(key), derivedKey);
// byte[] encryptedKey = encryptor.Encrypt(key, derivedKey);
// byte[] decryptedKey = decryptor.Decrypt(encryptedKey, derivedKey);
// Array.Clear(encryptedKey, 0, encryptedKey.Length);
// using VaultDbContext context = dbContextFactory.CreateDbContext();
// context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={Convert.ToBase64String(decryptedKey)}");
// try
// {
// await context.Database.EnsureCreatedAsync(cancellationToken);
// writer.Write(args =>
// {
// var f = args.Name;
// });
// }
// catch
// {
// }
// Array.Clear(decryptedKey, 0, decryptedKey.Length);
// // Derive the key for encryption
// //byte[] encryptionKey = deriver.DeriveKey(password, salt);
// //// Derive the key for decryption
// //byte[] decryptionKey = deriver.DeriveKey(password, salt);
// //// Compare keys to ensure they're the same
// //bool areKeysEqual = encryptionKey.SequenceEqual(decryptionKey);
// ////byte[] derivedKey = deriver.DeriveKey(password, salt);
// //string? encrypted = encryptor.Encrypt(password, derivedKey);
// //var storedSalt = Convert.ToBase64String(salt);
byte[] derivedKey2 = deriver.DeriveKey(password, salt);
var dod = decryptor.Decrypt(encryptedKey, derivedKey2);
// //byte[] derivedKey2 = deriver.DeriveKey(password, salt);
// Derive the key for encryption
// //var d = decryptor.Decrypt(encrypted, derivedKey2);
//byte[] encryptionKey = deriver.DeriveKey(password, salt);
// // Generate a hash
// //string hash = hasher.HashPassword(password);
//// Derive the key for decryption
//byte[] decryptionKey = deriver.DeriveKey(password, salt);
// //string[] parts = hash.Split(':');
//// Compare keys to ensure they're the same
//bool areKeysEqual = encryptionKey.SequenceEqual(decryptionKey);
// //// Store the salt only
// //string storedSalt = parts[0];
////byte[] derivedKey = deriver.DeriveKey(password, salt);
//string? encrypted = encryptor.Encrypt(password, derivedKey);
// //// Use the hash as the password
// //string storedHash = parts[1];
//var storedSalt = Convert.ToBase64String(salt);
// //context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={storedHash}");
// //await context.Database.EnsureCreatedAsync();
// return true;
// }
// }
//byte[] derivedKey2 = deriver.DeriveKey(password, salt);
//var d = decryptor.Decrypt(encrypted, derivedKey2);
// Generate a hash
//string hash = hasher.HashPassword(password);
//string[] parts = hash.Split(':');
//// Store the salt only
//string storedSalt = parts[0];
//// Use the hash as the password
//string storedHash = parts[1];
//context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={storedHash}");
//await context.Database.EnsureCreatedAsync();
return true;
}
}
return false;
}
}
// return false;
// }
//}
+5 -5
View File
@@ -1,7 +1,7 @@
namespace Bitvault
namespace Bitvault;
public interface IDecryptor
{
public interface IDecryptor
{
string? Decrypt(string cipherText, byte[] key);
}
byte[] Decrypt(byte[] cipher,
byte[] key);
}
+5 -6
View File
@@ -1,8 +1,7 @@
namespace Bitvault
{
public interface IEncryptor
{
namespace Bitvault;
string? Encrypt(string plainText, byte[] key);
}
public interface IEncryptor
{
byte[] Encrypt(byte[] data, byte[] key);
}
+6
View File
@@ -0,0 +1,6 @@
namespace Bitvault;
public interface IKeyGenerator
{
byte[] Generate(int size);
}
+9
View File
@@ -0,0 +1,9 @@
using Toolkit.Foundation;
namespace Bitvault
{
public interface IVaultComponentFactory
{
IComponentHost? Create(string name);
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace Bitvault
{
public interface IVaultFactory
{
bool Create(string name, string password);
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace Bitvault;
public interface IVaultKeyGenerator
{
VaultKey Create(string password);
}
+6
View File
@@ -0,0 +1,6 @@
namespace Bitvault;
public interface IVaultStorage
{
bool Create(string name, VaultKey key);
}
+14
View File
@@ -0,0 +1,14 @@
using System.Security.Cryptography;
namespace Bitvault;
public class KeyGenerator :
IKeyGenerator
{
public byte[] Generate(int size)
{
byte[] key = new byte[size];
RandomNumberGenerator.Fill(key);
return key;
}
}
+4 -4
View File
@@ -12,10 +12,10 @@ public class OpenVaultHandler(IMediator mediator) :
{
if (vault.Password is { Length: > 0 } password)
{
if (await mediator.Handle<Open<VaultStorage>, bool>(Open.As(new VaultStorage("Personal", password)), cancellationToken))
{
return true;
}
//if (await mediator.Handle<Open<VaultStorage>, bool>(Open.As(new VaultStorage("Personal", password)), cancellationToken))
//{
// return true;
//}
}
}
+35 -35
View File
@@ -1,41 +1,41 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Toolkit.Foundation;
//using Microsoft.EntityFrameworkCore;
//using Microsoft.Extensions.Hosting;
//using Toolkit.Foundation;
namespace Bitvault;
//namespace Bitvault;
public class OpenVaultStorageHandler(IHostEnvironment environment,
IDbContextFactory<VaultDbContext> dbContextFactory) : IHandler<Open<VaultStorage>, bool>
{
public async Task<bool> Handle(Open<VaultStorage> args, CancellationToken cancellationToken)
{
if (args.Value is VaultStorage vault)
{
if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password)
{
using VaultDbContext context = dbContextFactory.CreateDbContext();
var d = context.Database.GetDbConnection().ConnectionString;
context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={password}");
//public class OpenVaultStorageHandler(IHostEnvironment environment,
// IDbContextFactory<VaultDbContext> dbContextFactory) : IHandler<Open<VaultStorage>, bool>
//{
// public async Task<bool> Handle(Open<VaultStorage> args, CancellationToken cancellationToken)
// {
// if (args.Value is VaultStorage vault)
// {
// if (vault.Name is { Length: > 0 } name && vault.Password is { Length: > 0 } password)
// {
// using VaultDbContext context = dbContextFactory.CreateDbContext();
// var d = context.Database.GetDbConnection().ConnectionString;
// context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}.vault;Mode=ReadWriteCreate;Password={password}");
bool isOpen = false;
await Task.Run(async () =>
{
try
{
await context.Database.OpenConnectionAsync();
isOpen = true;
}
catch
{
// We are ignoring this exception as it is either a go, or not.
}
// bool isOpen = false;
// await Task.Run(async () =>
// {
// try
// {
// await context.Database.OpenConnectionAsync();
// isOpen = true;
// }
// catch
// {
// // We are ignoring this exception as it is either a go, or not.
// }
}, cancellationToken);
// }, cancellationToken);
return isOpen;
}
}
// return isOpen;
// }
// }
return false;
}
}
// return false;
// }
//}
+18
View File
@@ -0,0 +1,18 @@
using Toolkit.Foundation;
namespace Bitvault;
public class VaultComponentFactory(IComponentFactory componentFactory) :
IVaultComponentFactory
{
public IComponentHost? Create(string name)
{
if (componentFactory.Create<IVaultComponent, VaultConfiguration>($"Vault:{name}",
new VaultConfiguration { Name = name }) is IComponentHost host)
{
return host;
}
return default;
}
}
+2
View File
@@ -5,4 +5,6 @@ namespace Bitvault;
public record VaultConfiguration : ComponentConfiguration
{
public string? Name { get; set; }
public string? Key { get; set; }
}
+13
View File
@@ -0,0 +1,13 @@
namespace Bitvault;
public class VaultFactory(IVaultKeyGenerator keyGenerator,
IVaultStorage vaultStorageFactory) :
IVaultFactory
{
public bool Create(string name,
string password)
{
VaultKey key = keyGenerator.Create(password);
return vaultStorageFactory.Create(name, key);
}
}
+3
View File
@@ -0,0 +1,3 @@
namespace Bitvault;
public record VaultKey(byte[] Salt, byte[] Public, byte[] Private);
+21
View File
@@ -0,0 +1,21 @@
namespace Bitvault;
public class VaultKeyGenerator(IKeyGenerator generator,
IKeyDeriver deriver,
IEncryptor encryptor,
IDecryptor decryptor) :
IVaultKeyGenerator
{
public VaultKey Create(string password)
{
byte[] salt = generator.Generate(16);
byte[] key = generator.Generate(32);
byte[] derivedKey = deriver.DeriveKey(password, salt);
byte[] publicKey = encryptor.Encrypt(key, derivedKey);
byte[] privateKey = decryptor.Decrypt(publicKey, derivedKey);
return new VaultKey(salt, publicKey, privateKey);
}
}
+17 -2
View File
@@ -1,3 +1,18 @@
namespace Bitvault;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
public record VaultStorage(string Name, string Password);
namespace Bitvault;
public class VaultStorage(IHostEnvironment environment,
IDbContextFactory<VaultDbContext> dbContextFactory) :
IVaultStorage
{
public bool Create(string name, VaultKey key)
{
using VaultDbContext context = dbContextFactory.CreateDbContext();
context.Database.SetConnectionString($"Data Source={Path.Combine(environment.ContentRootPath, name)}" +
$".vault;Mode=ReadWriteCreate;Password={Convert.ToBase64String(key.Private)}");
return true;
}
}
+1 -1
Submodule Toolkit updated: 7e980dbfce...b89f21b3ca