mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
[PM-2713] add auto unlock key to mobile
This commit is contained in:
@@ -323,6 +323,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (oldTimeout != newTimeout)
|
if (oldTimeout != newTimeout)
|
||||||
{
|
{
|
||||||
|
await _cryptoService.RefreshKeysAsync();
|
||||||
await Device.InvokeOnMainThreadAsync(BuildList);
|
await Device.InvokeOnMainThreadAsync(BuildList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<UserKey> MakeUserKeyAsync();
|
Task<UserKey> MakeUserKeyAsync();
|
||||||
Task ClearUserKeyAsync(string userId = null);
|
Task ClearUserKeyAsync(string userId = null);
|
||||||
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
||||||
|
Task<UserKey> GetAutoUnlockKeyAsync(string userId = null);
|
||||||
|
Task<bool> HasAutoUnlockKeyAsync(string userId = null);
|
||||||
Task SetMasterKeyAsync(MasterKey masterKey, string userId = null);
|
Task SetMasterKeyAsync(MasterKey masterKey, string userId = null);
|
||||||
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
||||||
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
|
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ namespace Bit.Core.Abstractions
|
|||||||
Task SetMasterKeyAsync(MasterKey value, string userId = null);
|
Task SetMasterKeyAsync(MasterKey value, string userId = null);
|
||||||
Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null);
|
Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null);
|
||||||
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
||||||
|
Task<UserKey> GetUserKeyAutoUnlockAsync(string userId = null);
|
||||||
|
Task SetUserKeyAutoUnlockAsync(string value, string userId = null);
|
||||||
Task<string> GetActiveUserIdAsync();
|
Task<string> GetActiveUserIdAsync();
|
||||||
Task<string> GetActiveUserEmailAsync();
|
Task<string> GetActiveUserEmailAsync();
|
||||||
Task<T> GetActiveUserCustomDataAsync<T>(Func<Account, T> dataMapper);
|
Task<T> GetActiveUserCustomDataAsync<T>(Func<Account, T> dataMapper);
|
||||||
@@ -190,6 +192,9 @@ namespace Bit.Core.Abstractions
|
|||||||
Task SetEncKeyEncryptedAsync(string value, string userId = null);
|
Task SetEncKeyEncryptedAsync(string value, string userId = null);
|
||||||
[Obsolete("Left for migration purposes")]
|
[Obsolete("Left for migration purposes")]
|
||||||
Task SetKeyEncryptedAsync(string value, string userId = null);
|
Task SetKeyEncryptedAsync(string value, string userId = null);
|
||||||
|
|
||||||
|
[Obsolete("Use GetUserKeyAutoUnlock instead, left for migration purposes")]
|
||||||
|
Task<string> GetKeyEncryptedAsync(string userId = null);
|
||||||
[Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")]
|
[Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")]
|
||||||
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ namespace Bit.Core
|
|||||||
public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
||||||
public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}";
|
public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}";
|
||||||
public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}";
|
public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}";
|
||||||
|
public static string UserKeyAutoUnlockKey(string userId) => $"autoUnlock_{userId}";
|
||||||
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
||||||
public static string FoldersKey(string userId) => $"folders_{userId}";
|
public static string FoldersKey(string userId) => $"folders_{userId}";
|
||||||
public static string CollectionsKey(string userId) => $"collections_{userId}";
|
public static string CollectionsKey(string userId) => $"collections_{userId}";
|
||||||
|
|||||||
@@ -45,24 +45,15 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task RefreshKeysAsync()
|
public async Task RefreshKeysAsync()
|
||||||
{
|
{
|
||||||
// refresh or clear the pin key
|
// Refresh or clear additional keys such as
|
||||||
|
// pin and auto unlock keys
|
||||||
await SetUserKeyAsync(await GetUserKeyAsync());
|
await SetUserKeyAsync(await GetUserKeyAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetUserKeyAsync(UserKey userKey, string userId = null)
|
public async Task SetUserKeyAsync(UserKey userKey, string userId = null)
|
||||||
{
|
{
|
||||||
await _stateService.SetUserKeyAsync(userKey, userId);
|
await _stateService.SetUserKeyAsync(userKey, userId);
|
||||||
|
await StoreAdditionalKeysAsync(userKey, userId);
|
||||||
// Refresh the Pin Key if the user has a Pin set
|
|
||||||
if (await _stateService.GetProtectedPinAsync(userId) != null)
|
|
||||||
{
|
|
||||||
await UpdateUserKeyPinAsync(userKey, userId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId);
|
|
||||||
await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<UserKey> GetUserKeyAsync(string userId = null)
|
public Task<UserKey> GetUserKeyAsync(string userId = null)
|
||||||
@@ -108,6 +99,17 @@ namespace Bit.Core.Services
|
|||||||
return _stateService.SetMasterKeyEncryptedUserKeyAsync(value, userId);
|
return _stateService.SetMasterKeyEncryptedUserKeyAsync(value, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<UserKey> GetAutoUnlockKeyAsync(string userId = null)
|
||||||
|
{
|
||||||
|
await MigrateAutoUnlockKeyIfNeededAsync(userId);
|
||||||
|
return await _stateService.GetUserKeyAutoUnlockAsync(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HasAutoUnlockKeyAsync(string userId = null)
|
||||||
|
{
|
||||||
|
return (await GetAutoUnlockKeyAsync(userId) != null);
|
||||||
|
}
|
||||||
|
|
||||||
public Task SetMasterKeyAsync(MasterKey masterKey, string userId = null)
|
public Task SetMasterKeyAsync(MasterKey masterKey, string userId = null)
|
||||||
{
|
{
|
||||||
return _stateService.SetMasterKeyAsync(masterKey, userId);
|
return _stateService.SetMasterKeyAsync(masterKey, userId);
|
||||||
@@ -666,6 +668,30 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
// --HELPER METHODS--
|
// --HELPER METHODS--
|
||||||
|
|
||||||
|
private async Task StoreAdditionalKeysAsync(UserKey userKey, string userId = null)
|
||||||
|
{
|
||||||
|
// Refresh, set, or clear the pin key
|
||||||
|
if (await _stateService.GetProtectedPinAsync(userId) != null)
|
||||||
|
{
|
||||||
|
await UpdateUserKeyPinAsync(userKey, userId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId);
|
||||||
|
await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh, set, or clear the auto key
|
||||||
|
if (await _stateService.GetVaultTimeoutAsync(userId) == null)
|
||||||
|
{
|
||||||
|
await _stateService.SetUserKeyAutoUnlockAsync(userKey.KeyB64, userId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _stateService.SetUserKeyAutoUnlockAsync(null, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateUserKeyPinAsync(UserKey userKey, string userId = null)
|
private async Task UpdateUserKeyPinAsync(UserKey userKey, string userId = null)
|
||||||
{
|
{
|
||||||
var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId)));
|
var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId)));
|
||||||
@@ -930,6 +956,27 @@ namespace Bit.Core.Services
|
|||||||
// We previously used the master key for additional keys, but now we use the user key.
|
// We previously used the master key for additional keys, but now we use the user key.
|
||||||
// These methods support migrating the old keys to the new ones.
|
// These methods support migrating the old keys to the new ones.
|
||||||
|
|
||||||
|
private async Task MigrateAutoUnlockKeyIfNeededAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var oldAutoKey = await _stateService.GetKeyEncryptedAsync(userId);
|
||||||
|
if (oldAutoKey == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Decrypt
|
||||||
|
var masterKey = new MasterKey(Convert.FromBase64String(oldAutoKey));
|
||||||
|
var encryptedUserKey = await _stateService.GetEncKeyEncryptedAsync(userId);
|
||||||
|
var userKey = await DecryptUserKeyWithMasterKeyAsync(
|
||||||
|
masterKey,
|
||||||
|
new EncString(encryptedUserKey),
|
||||||
|
userId);
|
||||||
|
// Migrate
|
||||||
|
await _stateService.SetUserKeyAutoUnlockAsync(userKey.KeyB64, userId);
|
||||||
|
await _stateService.SetKeyEncryptedAsync(null, userId);
|
||||||
|
// Set encrypted user key just in case the user locks without syncing
|
||||||
|
await SetMasterKeyEncryptedUserKeyAsync(encryptedUserKey);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<UserKey> DecryptAndMigrateOldPinKeyAsync(
|
public async Task<UserKey> DecryptAndMigrateOldPinKeyAsync(
|
||||||
bool masterPasswordOnRestart,
|
bool masterPasswordOnRestart,
|
||||||
string pin,
|
string pin,
|
||||||
|
|||||||
@@ -336,12 +336,27 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null)
|
public async Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null)
|
||||||
{
|
{
|
||||||
return await _storageMediatorService.GetAsync<string>(Constants.MasterKeyEncryptedUserKeyKey(userId), false);
|
return await _storageMediatorService.GetAsync<string>(
|
||||||
|
await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null)
|
public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null)
|
||||||
{
|
{
|
||||||
await _storageMediatorService.SaveAsync(Constants.MasterKeyEncryptedUserKeyKey(userId), value, false);
|
await _storageMediatorService.SaveAsync(
|
||||||
|
await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserKey> GetUserKeyAutoUnlockAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var keyB64 = await _storageMediatorService.GetAsync<string>(
|
||||||
|
await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), true);
|
||||||
|
return keyB64 == null ? null : new UserKey(Convert.FromBase64String(keyB64));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetUserKeyAutoUnlockAsync(string value, string userId = null)
|
||||||
|
{
|
||||||
|
await _storageMediatorService.SaveAsync(
|
||||||
|
await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
||||||
@@ -350,6 +365,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
userId = await GetActiveUserIdAsync();
|
userId = await GetActiveUserIdAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await IsAuthenticatedAsync(userId))
|
if (!await IsAuthenticatedAsync(userId))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -397,13 +413,15 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<EncString> GetPinKeyEncryptedUserKeyAsync(string userId = null)
|
public async Task<EncString> GetPinKeyEncryptedUserKeyAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var key = await _storageMediatorService.GetAsync<string>(Constants.PinKeyEncryptedUserKeyKey(userId), false);
|
var key = await _storageMediatorService.GetAsync<string>(
|
||||||
|
await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), false);
|
||||||
return key != null ? new EncString(key) : null;
|
return key != null ? new EncString(key) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null)
|
public async Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null)
|
||||||
{
|
{
|
||||||
await _storageMediatorService.SaveAsync(Constants.PinKeyEncryptedUserKeyKey(userId), value?.EncryptedString, false);
|
await _storageMediatorService.SaveAsync(
|
||||||
|
await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), value?.EncryptedString, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EncString> GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null)
|
public async Task<EncString> GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null)
|
||||||
@@ -1445,28 +1463,30 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Non-state storage
|
// Non-state storage
|
||||||
await SetProtectedPinAsync(null, userId);
|
await Task.WhenAll(
|
||||||
await SetPinProtectedAsync(null, userId);
|
SetUserKeyAutoUnlockAsync(null, userId),
|
||||||
await SetKeyEncryptedAsync(null, userId);
|
SetProtectedPinAsync(null, userId),
|
||||||
await SetKeyHashAsync(null, userId);
|
SetKeyHashAsync(null, userId),
|
||||||
await SetEncKeyEncryptedAsync(null, userId);
|
SetOrgKeysEncryptedAsync(null, userId),
|
||||||
await SetOrgKeysEncryptedAsync(null, userId);
|
SetPrivateKeyEncryptedAsync(null, userId),
|
||||||
await SetPrivateKeyEncryptedAsync(null, userId);
|
SetLastActiveTimeAsync(null, userId),
|
||||||
await SetLastActiveTimeAsync(null, userId);
|
SetPreviousPageInfoAsync(null, userId),
|
||||||
await SetPreviousPageInfoAsync(null, userId);
|
SetInvalidUnlockAttemptsAsync(null, userId),
|
||||||
await SetInvalidUnlockAttemptsAsync(null, userId);
|
SetLocalDataAsync(null, userId),
|
||||||
await SetLocalDataAsync(null, userId);
|
SetEncryptedCiphersAsync(null, userId),
|
||||||
await SetEncryptedCiphersAsync(null, userId);
|
SetEncryptedCollectionsAsync(null, userId),
|
||||||
await SetEncryptedCollectionsAsync(null, userId);
|
SetLastSyncAsync(null, userId),
|
||||||
await SetLastSyncAsync(null, userId);
|
SetEncryptedFoldersAsync(null, userId),
|
||||||
await SetEncryptedFoldersAsync(null, userId);
|
SetEncryptedPoliciesAsync(null, userId),
|
||||||
await SetEncryptedPoliciesAsync(null, userId);
|
SetUsesKeyConnectorAsync(null, userId),
|
||||||
await SetUsesKeyConnectorAsync(null, userId);
|
SetOrganizationsAsync(null, userId),
|
||||||
await SetOrganizationsAsync(null, userId);
|
SetEncryptedPasswordGenerationHistoryAsync(null, userId),
|
||||||
await SetEncryptedPasswordGenerationHistoryAsync(null, userId);
|
SetEncryptedSendsAsync(null, userId),
|
||||||
await SetEncryptedSendsAsync(null, userId);
|
SetSettingsAsync(null, userId),
|
||||||
await SetSettingsAsync(null, userId);
|
SetApprovePasswordlessLoginsAsync(null, userId),
|
||||||
await SetApprovePasswordlessLoginsAsync(null, userId);
|
SetEncKeyEncryptedAsync(null, userId),
|
||||||
|
SetKeyEncryptedAsync(null, userId),
|
||||||
|
SetPinProtectedAsync(null, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ScaffoldNewAccountAsync(Account account)
|
private async Task ScaffoldNewAccountAsync(Account account)
|
||||||
@@ -1713,6 +1733,14 @@ namespace Bit.Core.Services
|
|||||||
await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use GetUserKeyAutoUnlock instead, left for migration purposes")]
|
||||||
|
public async Task<string> GetKeyEncryptedAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultSecureStorageOptionsAsync());
|
||||||
|
return await GetValueAsync<string>(Constants.KeyKey(reconciledOptions.UserId), reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
[Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")]
|
[Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")]
|
||||||
public async Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null)
|
public async Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,15 +59,26 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<bool> IsLockedAsync(string userId = null)
|
public async Task<bool> IsLockedAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasUserKeyAsync(userId);
|
var biometricSet = await IsBiometricLockSetAsync(userId);
|
||||||
if (hasKey)
|
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
|
||||||
{
|
{
|
||||||
var biometricSet = await IsBiometricLockSetAsync(userId);
|
return true;
|
||||||
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
|
}
|
||||||
|
|
||||||
|
if (!await _cryptoService.HasUserKeyAsync(userId))
|
||||||
|
{
|
||||||
|
if (await _cryptoService.HasAutoUnlockKeyAsync(userId))
|
||||||
|
{
|
||||||
|
await _cryptoService.SetUserKeyAsync(await _cryptoService.GetAutoUnlockKeyAsync(userId));
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check again to verify auto key was set
|
||||||
|
var hasKey = await _cryptoService.HasUserKeyAsync(userId);
|
||||||
return !hasKey;
|
return !hasKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +207,7 @@ namespace Bit.Core.Services
|
|||||||
await Task.WhenAll(
|
await Task.WhenAll(
|
||||||
_cryptoService.ClearUserKeyAsync(userId),
|
_cryptoService.ClearUserKeyAsync(userId),
|
||||||
_cryptoService.ClearMasterKeyAsync(userId),
|
_cryptoService.ClearMasterKeyAsync(userId),
|
||||||
|
_stateService.SetUserKeyAutoUnlockAsync(null, userId),
|
||||||
_cryptoService.ClearOrgKeysAsync(true, userId),
|
_cryptoService.ClearOrgKeysAsync(true, userId),
|
||||||
_cryptoService.ClearKeyPairAsync(true, userId));
|
_cryptoService.ClearKeyPairAsync(true, userId));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user