mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-02 05:46:39 -05:00
Add local encryption key validation to unlock page (#200)
This commit is contained in:
@@ -385,7 +385,7 @@ else
|
||||
await AuthService.StoreRefreshTokenAsync(validateLoginResponse.Token!.RefreshToken);
|
||||
|
||||
// Store the encryption key in memory.
|
||||
AuthService.StoreEncryptionKey(PasswordHash);
|
||||
await AuthService.StoreEncryptionKeyAsync(PasswordHash);
|
||||
|
||||
await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
GlobalNotificationService.ClearMessages();
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
if (tokenObject != null)
|
||||
{
|
||||
// Store the encryption key in memory.
|
||||
AuthService.StoreEncryptionKey(passwordHash);
|
||||
await AuthService.StoreEncryptionKeyAsync(passwordHash);
|
||||
|
||||
// Store the token as a plain string in local storage
|
||||
await AuthService.StoreAccessTokenAsync(tokenObject.Token);
|
||||
|
||||
@@ -101,8 +101,18 @@
|
||||
// 3. Client derives shared session key.
|
||||
byte[] passwordHash = await Encryption.DeriveKeyFromPasswordAsync(UnlockModel.Password, loginResponse!.Salt);
|
||||
|
||||
// Check if the password is correct locally by decrypting the test string.
|
||||
var validPassword = await AuthService.ValidateEncryptionKeyAsync(passwordHash);
|
||||
|
||||
if (!validPassword)
|
||||
{
|
||||
ServerValidationErrors.AddError("The password is incorrect. Please try entering your password again, or logout and login again.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the encryption key in memory.
|
||||
AuthService.StoreEncryptionKey(passwordHash);
|
||||
await AuthService.StoreEncryptionKeyAsync(passwordHash);
|
||||
|
||||
|
||||
// Redirect to the page the user was trying to access before if set.
|
||||
var localStorageReturnUrl = await LocalStorage.GetItemAsync<string>("returnUrl");
|
||||
|
||||
@@ -151,7 +151,7 @@ else
|
||||
|
||||
// Set new currentPasswordHash locally as it is required for the new database encryption call below so
|
||||
// it is encrypted with the new password hash.
|
||||
AuthService.StoreEncryptionKey(newPasswordHash);
|
||||
await AuthService.StoreEncryptionKeyAsync(newPasswordHash);
|
||||
|
||||
var srpPasswordChange = Srp.PasswordChangeAsync(client, newSalt, username, newPasswordHashString);
|
||||
|
||||
@@ -182,8 +182,8 @@ else
|
||||
// Clear form.
|
||||
PasswordChangeModel = new PasswordChangeModel();
|
||||
|
||||
// Set currentPasswordHash back to original so we're back to the original state.
|
||||
AuthService.StoreEncryptionKey(backupPasswordHash);
|
||||
// Set currentPasswordHash back to original, so we're back to the original state.
|
||||
await AuthService.StoreEncryptionKeyAsync(backupPasswordHash);
|
||||
|
||||
GlobalLoadingSpinner.Hide();
|
||||
StateHasChanged();
|
||||
|
||||
@@ -21,10 +21,18 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
/// <param name="localStorage">The local storage service.</param>
|
||||
/// <param name="environment">IWebAssemblyHostEnvironment instance.</param>
|
||||
/// <param name="configuration">IConfiguration instance.</param>
|
||||
public sealed class AuthService(HttpClient httpClient, ILocalStorageService localStorage, IWebAssemblyHostEnvironment environment, IConfiguration configuration)
|
||||
/// <param name="jsInteropService">JSInteropService instance.</param>
|
||||
public sealed class AuthService(HttpClient httpClient, ILocalStorageService localStorage, IWebAssemblyHostEnvironment environment, IConfiguration configuration, JsInteropService jsInteropService)
|
||||
{
|
||||
private const string AccessTokenKey = "token";
|
||||
private const string RefreshTokenKey = "refreshToken";
|
||||
|
||||
/// <summary>
|
||||
/// Test string that is stored in local storage in encrypted state. This is used to validate the encryption key
|
||||
/// locally during future vault unlocks.
|
||||
/// </summary>
|
||||
private const string EncryptionTestString = "aliasvault-test-string";
|
||||
|
||||
private byte[] _encryptionKey = new byte[32];
|
||||
|
||||
/// <summary>
|
||||
@@ -139,9 +147,48 @@ public sealed class AuthService(HttpClient httpClient, ILocalStorageService loca
|
||||
/// Stores the encryption key asynchronously in-memory.
|
||||
/// </summary>
|
||||
/// <param name="newKey">SrpArgonEncryption key.</param>
|
||||
public void StoreEncryptionKey(byte[] newKey)
|
||||
/// <returns>Task.</returns>
|
||||
public async Task StoreEncryptionKeyAsync(byte[] newKey)
|
||||
{
|
||||
_encryptionKey = newKey;
|
||||
|
||||
// When storing a new encryption key, encrypt a test string and save it to local storage.
|
||||
// This test string can then be used to locally validate the password during future unlocks.
|
||||
var encryptedTestString = await jsInteropService.SymmetricEncrypt(EncryptionTestString, GetEncryptionKeyAsBase64Async());
|
||||
|
||||
// Store the encrypted test string in local storage.
|
||||
await localStorage.SetItemAsStringAsync("encryptionTestString", encryptedTestString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate the encryption locally by attempting to decrypt test string stored in local storage.
|
||||
/// </summary>
|
||||
/// <param name="encryptionKey">The encryption key to validate.</param>
|
||||
/// <returns>True if encryption key is valid, false if not.</returns>
|
||||
public async Task<bool> ValidateEncryptionKeyAsync(byte[] encryptionKey)
|
||||
{
|
||||
// Get the encrypted test string from local storage.
|
||||
var encryptedTestString = await localStorage.GetItemAsStringAsync("encryptionTestString");
|
||||
if (encryptedTestString == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var base64EncryptionKey = Convert.ToBase64String(encryptionKey);
|
||||
|
||||
// Decrypt the test string using the provided encryption key.
|
||||
try
|
||||
{
|
||||
var decryptedTestString = await jsInteropService.SymmetricDecrypt(encryptedTestString, base64EncryptionKey);
|
||||
|
||||
// If the decrypted test string is not equal to the test string, the encryption key is invalid.
|
||||
return decryptedTestString == EncryptionTestString;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors, if decryption fails the encryption key is invalid.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -140,6 +140,7 @@ public sealed class DbService : IDisposable
|
||||
|
||||
// Make sure a public/private RSA encryption key exists before saving the database.
|
||||
await GetOrCreateEncryptionKeyAsync();
|
||||
|
||||
var encryptedBase64String = await GetEncryptedDatabaseBase64String();
|
||||
|
||||
// Save to webapi.
|
||||
|
||||
Reference in New Issue
Block a user