mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
[PM-1817] Expand biometric integrity checks to the account level (#2498)
* Change bio integrity validation to work at account-level * biometric state migration * fix account bio valid key storage location during migration * comment clarification * fix for iOS extensions not using custom avatar color
This commit is contained in:
parent
4f0238122b
commit
0f417b8434
22 changed files with 236 additions and 102 deletions
|
@ -21,6 +21,7 @@ using Bit.App.Utilities;
|
||||||
using Bit.App.Pages;
|
using Bit.App.Pages;
|
||||||
using Bit.App.Utilities.AccountManagement;
|
using Bit.App.Utilities.AccountManagement;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
|
using Bit.Core.Enums;
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
using Android.Gms.Security;
|
using Android.Gms.Security;
|
||||||
#endif
|
#endif
|
||||||
|
@ -147,7 +148,7 @@ namespace Bit.Droid
|
||||||
var storageMediatorService = new StorageMediatorService(mobileStorageService, secureStorageService, preferencesStorage);
|
var storageMediatorService = new StorageMediatorService(mobileStorageService, secureStorageService, preferencesStorage);
|
||||||
var stateService = new StateService(mobileStorageService, secureStorageService, storageMediatorService, messagingService);
|
var stateService = new StateService(mobileStorageService, secureStorageService, storageMediatorService, messagingService);
|
||||||
var stateMigrationService =
|
var stateMigrationService =
|
||||||
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
new StateMigrationService(DeviceType.Android, liteDbStorage, preferencesStorage, secureStorageService);
|
||||||
var clipboardService = new ClipboardService(stateService);
|
var clipboardService = new ClipboardService(stateService);
|
||||||
var deviceActionService = new DeviceActionService(stateService, messagingService);
|
var deviceActionService = new DeviceActionService(stateService, messagingService);
|
||||||
var fileService = new FileService(stateService, broadcasterService);
|
var fileService = new FileService(stateService, broadcasterService);
|
||||||
|
@ -155,7 +156,7 @@ namespace Bit.Droid
|
||||||
messagingService, broadcasterService);
|
messagingService, broadcasterService);
|
||||||
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
|
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
|
||||||
platformUtilsService, new LazyResolve<IEventService>());
|
platformUtilsService, new LazyResolve<IEventService>());
|
||||||
var biometricService = new BiometricService();
|
var biometricService = new BiometricService(stateService);
|
||||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||||
|
|
|
@ -4,7 +4,6 @@ using Android.OS;
|
||||||
using Android.Security.Keystore;
|
using Android.Security.Keystore;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Java.Security;
|
using Java.Security;
|
||||||
using Javax.Crypto;
|
using Javax.Crypto;
|
||||||
|
|
||||||
|
@ -12,6 +11,8 @@ namespace Bit.Droid.Services
|
||||||
{
|
{
|
||||||
public class BiometricService : IBiometricService
|
public class BiometricService : IBiometricService
|
||||||
{
|
{
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
private const string KeyName = "com.8bit.bitwarden.biometric_integrity";
|
private const string KeyName = "com.8bit.bitwarden.biometric_integrity";
|
||||||
|
|
||||||
private const string KeyStoreName = "AndroidKeyStore";
|
private const string KeyStoreName = "AndroidKeyStore";
|
||||||
|
@ -23,28 +24,28 @@ namespace Bit.Droid.Services
|
||||||
|
|
||||||
private readonly KeyStore _keystore;
|
private readonly KeyStore _keystore;
|
||||||
|
|
||||||
public BiometricService()
|
public BiometricService(IStateService stateService)
|
||||||
{
|
{
|
||||||
|
_stateService = stateService;
|
||||||
_keystore = KeyStore.GetInstance(KeyStoreName);
|
_keystore = KeyStore.GetInstance(KeyStoreName);
|
||||||
_keystore.Load(null);
|
_keystore.Load(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> SetupBiometricAsync(string bioIntegrityKey = null)
|
public async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
// bioIntegrityKey used in iOS only
|
|
||||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
|
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
|
||||||
{
|
{
|
||||||
CreateKey();
|
await CreateKeyAsync(bioIntegritySrcKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(true);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null)
|
public async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
|
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
|
||||||
{
|
{
|
||||||
return Task.FromResult(true);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -55,7 +56,7 @@ namespace Bit.Droid.Services
|
||||||
|
|
||||||
if (key == null || cipher == null)
|
if (key == null || cipher == null)
|
||||||
{
|
{
|
||||||
return Task.FromResult(true);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cipher.Init(CipherMode.EncryptMode, key);
|
cipher.Init(CipherMode.EncryptMode, key);
|
||||||
|
@ -63,25 +64,32 @@ namespace Bit.Droid.Services
|
||||||
catch (KeyPermanentlyInvalidatedException e)
|
catch (KeyPermanentlyInvalidatedException e)
|
||||||
{
|
{
|
||||||
// Biometric has changed
|
// Biometric has changed
|
||||||
return Task.FromResult(false);
|
await ClearStateAsync(bioIntegritySrcKey);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
catch (UnrecoverableKeyException e)
|
catch (UnrecoverableKeyException e)
|
||||||
{
|
{
|
||||||
// Biometric was disabled and re-enabled
|
// Biometric was disabled and re-enabled
|
||||||
return Task.FromResult(false);
|
await ClearStateAsync(bioIntegritySrcKey);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
catch (InvalidKeyException e)
|
catch (InvalidKeyException e)
|
||||||
{
|
{
|
||||||
// Fallback for old bitwarden users without a key
|
// Fallback for old bitwarden users without a key
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(e);
|
LoggerHelper.LogEvenIfCantBeResolved(e);
|
||||||
CreateKey();
|
await CreateKeyAsync(bioIntegritySrcKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(true);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateKey()
|
private async Task CreateKeyAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
|
bioIntegritySrcKey ??= Core.Constants.BiometricIntegritySourceKey;
|
||||||
|
await _stateService.SetSystemBiometricIntegrityState(bioIntegritySrcKey,
|
||||||
|
await GetStateAsync(bioIntegritySrcKey));
|
||||||
|
await _stateService.SetAccountBiometricIntegrityValidAsync(bioIntegritySrcKey);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var keyGen = KeyGenerator.GetInstance(KeyAlgorithm, KeyStoreName);
|
var keyGen = KeyGenerator.GetInstance(KeyAlgorithm, KeyStoreName);
|
||||||
|
@ -101,5 +109,16 @@ namespace Bit.Droid.Services
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(e);
|
LoggerHelper.LogEvenIfCantBeResolved(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetStateAsync(string bioIntegritySrcKey)
|
||||||
|
{
|
||||||
|
return await _stateService.GetSystemBiometricIntegrityState(bioIntegritySrcKey) ??
|
||||||
|
Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClearStateAsync(string bioIntegritySrcKey)
|
||||||
|
{
|
||||||
|
await _stateService.SetSystemBiometricIntegrityState(bioIntegritySrcKey, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout Padding="10, 0">
|
<StackLayout Padding="10, 0">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n BiometricInvalidated}"
|
Text="{u:I18n AccountBiometricInvalidated}"
|
||||||
StyleClass="box-footer-label,text-danger,text-bold"
|
StyleClass="box-footer-label,text-danger,text-bold"
|
||||||
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
|
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
|
||||||
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
|
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
|
||||||
|
|
|
@ -209,7 +209,7 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
if (BiometricLock)
|
if (BiometricLock)
|
||||||
{
|
{
|
||||||
BiometricIntegrityValid = await _biometricService.ValidateIntegrityAsync();
|
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
||||||
if (!_biometricIntegrityValid)
|
if (!_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
BiometricButtonVisible = false;
|
BiometricButtonVisible = false;
|
||||||
|
@ -431,7 +431,8 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
public async Task PromptBiometricAsync()
|
public async Task PromptBiometricAsync()
|
||||||
{
|
{
|
||||||
BiometricIntegrityValid = await _biometricService.ValidateIntegrityAsync();
|
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
||||||
|
BiometricButtonVisible = BiometricIntegrityValid;
|
||||||
if (!BiometricLock || !BiometricIntegrityValid)
|
if (!BiometricLock || !BiometricIntegrityValid)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
8
src/App/Resources/AppResources.Designer.cs
generated
8
src/App/Resources/AppResources.Designer.cs
generated
|
@ -970,18 +970,18 @@ namespace Bit.App.Resources {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Biometric unlock disabled pending verification of master password..
|
/// Looks up a localized string similar to Biometric unlock disabled pending verification of master password..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string BiometricInvalidated {
|
public static string AccountBiometricInvalidated {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("BiometricInvalidated", resourceCulture);
|
return ResourceManager.GetString("AccountBiometricInvalidated", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Biometric unlock for autofill disabled pending verification of master password..
|
/// Looks up a localized string similar to Biometric unlock for autofill disabled pending verification of master password..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string BiometricInvalidatedExtension {
|
public static string AccountBiometricInvalidatedExtension {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("BiometricInvalidatedExtension", resourceCulture);
|
return ResourceManager.GetString("AccountBiometricInvalidatedExtension", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1752,11 +1752,11 @@ Scanning will happen automatically.</value>
|
||||||
<value>Do you really want to send to the trash?</value>
|
<value>Do you really want to send to the trash?</value>
|
||||||
<comment>Confirmation alert message when soft-deleting a cipher.</comment>
|
<comment>Confirmation alert message when soft-deleting a cipher.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="BiometricInvalidated" xml:space="preserve">
|
<data name="AccountBiometricInvalidated" xml:space="preserve">
|
||||||
<value>Biometric unlock disabled pending verification of master password.</value>
|
<value>Biometric unlock for this account is disabled pending verification of master password.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="BiometricInvalidatedExtension" xml:space="preserve">
|
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve">
|
||||||
<value>Biometric unlock for autofill disabled pending verification of master password.</value>
|
<value>Autofill biometric unlock for this account is disabled pending verification of master password.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EnableSyncOnRefresh" xml:space="preserve">
|
<data name="EnableSyncOnRefresh" xml:space="preserve">
|
||||||
<value>Allow sync on refresh</value>
|
<value>Allow sync on refresh</value>
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Plugin.Fingerprint;
|
using Plugin.Fingerprint;
|
||||||
using Plugin.Fingerprint.Abstractions;
|
using Plugin.Fingerprint.Abstractions;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
|
@ -226,6 +227,20 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
|
||||||
|
{
|
||||||
|
bioIntegritySrcKey ??= Core.Constants.BiometricIntegritySourceKey;
|
||||||
|
|
||||||
|
var biometricService = ServiceContainer.Resolve<IBiometricService>();
|
||||||
|
if (!await biometricService.IsSystemBiometricIntegrityValidAsync(bioIntegritySrcKey))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stateService = ServiceContainer.Resolve<IStateService>();
|
||||||
|
return await stateService.IsAccountBiometricIntegrityValidAsync(bioIntegritySrcKey);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null,
|
public async Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null,
|
||||||
Action fallback = null)
|
Action fallback = null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,14 +22,14 @@ namespace Bit.App.Services
|
||||||
Constants.PushRegisteredTokenKey,
|
Constants.PushRegisteredTokenKey,
|
||||||
Constants.LastBuildKey,
|
Constants.LastBuildKey,
|
||||||
Constants.ClearCiphersCacheKey,
|
Constants.ClearCiphersCacheKey,
|
||||||
Constants.BiometricIntegrityKey,
|
Constants.BiometricIntegritySourceKey,
|
||||||
Constants.iOSExtensionActiveUserIdKey,
|
Constants.iOSExtensionActiveUserIdKey,
|
||||||
Constants.iOSAutoFillClearCiphersCacheKey,
|
Constants.iOSAutoFillClearCiphersCacheKey,
|
||||||
Constants.iOSAutoFillBiometricIntegrityKey,
|
Constants.iOSAutoFillBiometricIntegritySourceKey,
|
||||||
Constants.iOSExtensionClearCiphersCacheKey,
|
Constants.iOSExtensionClearCiphersCacheKey,
|
||||||
Constants.iOSExtensionBiometricIntegrityKey,
|
Constants.iOSExtensionBiometricIntegritySourceKey,
|
||||||
Constants.iOSShareExtensionClearCiphersCacheKey,
|
Constants.iOSShareExtensionClearCiphersCacheKey,
|
||||||
Constants.iOSShareExtensionBiometricIntegrityKey,
|
Constants.iOSShareExtensionBiometricIntegritySourceKey,
|
||||||
Constants.RememberedEmailKey,
|
Constants.RememberedEmailKey,
|
||||||
Constants.RememberedOrgIdentifierKey
|
Constants.RememberedOrgIdentifierKey
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Bit.Core.Abstractions
|
||||||
{
|
{
|
||||||
public interface IBiometricService
|
public interface IBiometricService
|
||||||
{
|
{
|
||||||
Task<bool> SetupBiometricAsync(string bioIntegrityKey = null);
|
Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null);
|
||||||
Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null);
|
Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace Bit.Core.Abstractions
|
||||||
bool SupportsFido2();
|
bool SupportsFido2();
|
||||||
bool SupportsDuo();
|
bool SupportsDuo();
|
||||||
Task<bool> SupportsBiometricAsync();
|
Task<bool> SupportsBiometricAsync();
|
||||||
|
Task<bool> IsBiometricIntegrityValidAsync(string bioIntegritySrcKey = null);
|
||||||
Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null, Action fallback = null);
|
Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null, Action fallback = null);
|
||||||
long GetActiveTime();
|
long GetActiveTime();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,10 @@ namespace Bit.Core.Abstractions
|
||||||
Task SetBiometricUnlockAsync(bool? value, string userId = null);
|
Task SetBiometricUnlockAsync(bool? value, string userId = null);
|
||||||
Task<bool> GetBiometricLockedAsync(string userId = null);
|
Task<bool> GetBiometricLockedAsync(string userId = null);
|
||||||
Task SetBiometricLockedAsync(bool value, string userId = null);
|
Task SetBiometricLockedAsync(bool value, string userId = null);
|
||||||
|
Task<string> GetSystemBiometricIntegrityState(string bioIntegritySrcKey);
|
||||||
|
Task SetSystemBiometricIntegrityState(string bioIntegritySrcKey, string systemBioIntegrityState);
|
||||||
|
Task<bool> IsAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null);
|
||||||
|
Task SetAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null);
|
||||||
Task<bool> CanAccessPremiumAsync(string userId = null);
|
Task<bool> CanAccessPremiumAsync(string userId = null);
|
||||||
Task SetPersonalPremiumAsync(bool value, string userId = null);
|
Task SetPersonalPremiumAsync(bool value, string userId = null);
|
||||||
Task<string> GetProtectedPinAsync(string userId = null);
|
Task<string> GetProtectedPinAsync(string userId = null);
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
public const string LastBuildKey = "lastBuild";
|
public const string LastBuildKey = "lastBuild";
|
||||||
public const string AddSitePromptShownKey = "addSitePromptShown";
|
public const string AddSitePromptShownKey = "addSitePromptShown";
|
||||||
public const string ClearCiphersCacheKey = "clearCiphersCache";
|
public const string ClearCiphersCacheKey = "clearCiphersCache";
|
||||||
public const string BiometricIntegrityKey = "biometricIntegrityState";
|
public const string BiometricIntegritySourceKey = "biometricIntegritySource";
|
||||||
public const string iOSAutoFillClearCiphersCacheKey = "iOSAutoFillClearCiphersCache";
|
public const string iOSAutoFillClearCiphersCacheKey = "iOSAutoFillClearCiphersCache";
|
||||||
public const string iOSAutoFillBiometricIntegrityKey = "iOSAutoFillBiometricIntegrityState";
|
public const string iOSAutoFillBiometricIntegritySourceKey = "iOSAutoFillBiometricIntegritySource";
|
||||||
public const string iOSExtensionClearCiphersCacheKey = "iOSExtensionClearCiphersCache";
|
public const string iOSExtensionClearCiphersCacheKey = "iOSExtensionClearCiphersCache";
|
||||||
public const string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState";
|
public const string iOSExtensionBiometricIntegritySourceKey = "iOSExtensionBiometricIntegritySource";
|
||||||
public const string iOSShareExtensionClearCiphersCacheKey = "iOSShareExtensionClearCiphersCache";
|
public const string iOSShareExtensionClearCiphersCacheKey = "iOSShareExtensionClearCiphersCache";
|
||||||
public const string iOSShareExtensionBiometricIntegrityKey = "iOSShareExtensionBiometricIntegrityState";
|
public const string iOSShareExtensionBiometricIntegritySourceKey = "iOSShareExtensionBiometricIntegritySource";
|
||||||
public const string iOSExtensionActiveUserIdKey = "iOSExtensionActiveUserId";
|
public const string iOSExtensionActiveUserIdKey = "iOSExtensionActiveUserId";
|
||||||
public const string EventCollectionKey = "eventCollection";
|
public const string EventCollectionKey = "eventCollection";
|
||||||
public const string RememberedEmailKey = "rememberedEmail";
|
public const string RememberedEmailKey = "rememberedEmail";
|
||||||
|
@ -110,6 +110,8 @@
|
||||||
public static string ProtectedPinKey(string userId) => $"protectedPin_{userId}";
|
public static string ProtectedPinKey(string userId) => $"protectedPin_{userId}";
|
||||||
public static string LastSyncKey(string userId) => $"lastSync_{userId}";
|
public static string LastSyncKey(string userId) => $"lastSync_{userId}";
|
||||||
public static string BiometricUnlockKey(string userId) => $"biometricUnlock_{userId}";
|
public static string BiometricUnlockKey(string userId) => $"biometricUnlock_{userId}";
|
||||||
|
public static string AccountBiometricIntegrityValidKey(string userId, string systemBioIntegrityState) =>
|
||||||
|
$"accountBiometricIntegrityValid_{userId}_{systemBioIntegrityState}";
|
||||||
public static string ApprovePasswordlessLoginsKey(string userId) => $"approvePasswordlessLogins_{userId}";
|
public static string ApprovePasswordlessLoginsKey(string userId) => $"approvePasswordlessLogins_{userId}";
|
||||||
public static string UsernameGenOptionsKey(string userId) => $"usernameGenerationOptions_{userId}";
|
public static string UsernameGenOptionsKey(string userId) => $"usernameGenerationOptions_{userId}";
|
||||||
public static string PushLastRegistrationDateKey(string userId) => $"pushLastRegistrationDate_{userId}";
|
public static string PushLastRegistrationDateKey(string userId) => $"pushLastRegistrationDate_{userId}";
|
||||||
|
|
|
@ -13,8 +13,9 @@ namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
public class StateMigrationService : IStateMigrationService
|
public class StateMigrationService : IStateMigrationService
|
||||||
{
|
{
|
||||||
private const int StateVersion = 4;
|
private const int StateVersion = 5;
|
||||||
|
|
||||||
|
private readonly DeviceType _deviceType;
|
||||||
private readonly IStorageService _preferencesStorageService;
|
private readonly IStorageService _preferencesStorageService;
|
||||||
private readonly IStorageService _liteDbStorageService;
|
private readonly IStorageService _liteDbStorageService;
|
||||||
private readonly IStorageService _secureStorageService;
|
private readonly IStorageService _secureStorageService;
|
||||||
|
@ -27,9 +28,10 @@ namespace Bit.Core.Services
|
||||||
Secure,
|
Secure,
|
||||||
}
|
}
|
||||||
|
|
||||||
public StateMigrationService(IStorageService liteDbStorageService, IStorageService preferenceStorageService,
|
public StateMigrationService(DeviceType deviceType, IStorageService liteDbStorageService,
|
||||||
IStorageService secureStorageService)
|
IStorageService preferenceStorageService, IStorageService secureStorageService)
|
||||||
{
|
{
|
||||||
|
_deviceType = deviceType;
|
||||||
_liteDbStorageService = liteDbStorageService;
|
_liteDbStorageService = liteDbStorageService;
|
||||||
_preferencesStorageService = preferenceStorageService;
|
_preferencesStorageService = preferenceStorageService;
|
||||||
_secureStorageService = secureStorageService;
|
_secureStorageService = secureStorageService;
|
||||||
|
@ -79,6 +81,9 @@ namespace Bit.Core.Services
|
||||||
case 3:
|
case 3:
|
||||||
await MigrateFrom3To4Async();
|
await MigrateFrom3To4Async();
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
await MigrateFrom4To5Async();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,6 +447,10 @@ namespace Bit.Core.Services
|
||||||
// Removal of old state data will happen organically as state is rebuilt in app
|
// Removal of old state data will happen organically as state is rebuilt in app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region v4 to v5 Migration
|
||||||
|
|
||||||
private class V4Keys
|
private class V4Keys
|
||||||
{
|
{
|
||||||
internal static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
internal static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
||||||
|
@ -450,6 +459,87 @@ namespace Bit.Core.Services
|
||||||
internal const string ThemeKey = "theme";
|
internal const string ThemeKey = "theme";
|
||||||
internal const string AutoDarkThemeKey = "autoDarkTheme";
|
internal const string AutoDarkThemeKey = "autoDarkTheme";
|
||||||
internal const string DisableFaviconKey = "disableFavicon";
|
internal const string DisableFaviconKey = "disableFavicon";
|
||||||
|
internal const string BiometricIntegrityKey = "biometricIntegrityState";
|
||||||
|
internal const string iOSAutoFillBiometricIntegrityKey = "iOSAutoFillBiometricIntegrityState";
|
||||||
|
internal const string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState";
|
||||||
|
internal const string iOSShareExtensionBiometricIntegrityKey = "iOSShareExtensionBiometricIntegrityState";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task MigrateFrom4To5Async()
|
||||||
|
{
|
||||||
|
var bioIntegrityState = await GetValueAsync<string>(Storage.Prefs, V4Keys.BiometricIntegrityKey);
|
||||||
|
var iOSAutofillBioIntegrityState =
|
||||||
|
await GetValueAsync<string>(Storage.Prefs, V4Keys.iOSAutoFillBiometricIntegrityKey);
|
||||||
|
var iOSExtensionBioIntegrityState =
|
||||||
|
await GetValueAsync<string>(Storage.Prefs, V4Keys.iOSExtensionBiometricIntegrityKey);
|
||||||
|
var iOSShareExtensionBioIntegrityState =
|
||||||
|
await GetValueAsync<string>(Storage.Prefs, V4Keys.iOSShareExtensionBiometricIntegrityKey);
|
||||||
|
|
||||||
|
if (_deviceType == DeviceType.Android && string.IsNullOrWhiteSpace(bioIntegrityState))
|
||||||
|
{
|
||||||
|
bioIntegrityState = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
await SetValueAsync(Storage.Prefs, V5Keys.BiometricIntegritySourceKey, bioIntegrityState);
|
||||||
|
|
||||||
|
if (_deviceType == DeviceType.iOS)
|
||||||
|
{
|
||||||
|
await SetValueAsync(Storage.Prefs, V5Keys.iOSAutoFillBiometricIntegritySourceKey,
|
||||||
|
iOSAutofillBioIntegrityState);
|
||||||
|
await SetValueAsync(Storage.Prefs, V5Keys.iOSExtensionBiometricIntegritySourceKey,
|
||||||
|
iOSExtensionBioIntegrityState);
|
||||||
|
await SetValueAsync(Storage.Prefs, V5Keys.iOSShareExtensionBiometricIntegritySourceKey,
|
||||||
|
iOSShareExtensionBioIntegrityState);
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = await GetValueAsync<State>(Storage.LiteDb, Constants.StateKey);
|
||||||
|
if (state?.Accounts is null)
|
||||||
|
{
|
||||||
|
// No accounts available, update stored version and exit
|
||||||
|
await SetLastStateVersionAsync(5);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build integrity keys for existing users
|
||||||
|
foreach (var account in state.Accounts.Where(a => a.Value?.Profile?.UserId != null))
|
||||||
|
{
|
||||||
|
var userId = account.Value.Profile.UserId;
|
||||||
|
|
||||||
|
await SetValueAsync(Storage.LiteDb,
|
||||||
|
V5Keys.AccountBiometricIntegrityValidKey(userId, bioIntegrityState), true);
|
||||||
|
|
||||||
|
if (_deviceType == DeviceType.iOS)
|
||||||
|
{
|
||||||
|
await SetValueAsync(Storage.LiteDb,
|
||||||
|
V5Keys.AccountBiometricIntegrityValidKey(userId, iOSAutofillBioIntegrityState), true);
|
||||||
|
await SetValueAsync(Storage.LiteDb,
|
||||||
|
V5Keys.AccountBiometricIntegrityValidKey(userId, iOSExtensionBioIntegrityState), true);
|
||||||
|
await SetValueAsync(Storage.LiteDb,
|
||||||
|
V5Keys.AccountBiometricIntegrityValidKey(userId, iOSShareExtensionBioIntegrityState), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update stored version
|
||||||
|
await SetLastStateVersionAsync(5);
|
||||||
|
|
||||||
|
// Remove old data
|
||||||
|
await RemoveValueAsync(Storage.Prefs, V4Keys.BiometricIntegrityKey);
|
||||||
|
await RemoveValueAsync(Storage.Prefs, V4Keys.iOSAutoFillBiometricIntegrityKey);
|
||||||
|
await RemoveValueAsync(Storage.Prefs, V4Keys.iOSExtensionBiometricIntegrityKey);
|
||||||
|
await RemoveValueAsync(Storage.Prefs, V4Keys.iOSShareExtensionBiometricIntegrityKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class V5Keys
|
||||||
|
{
|
||||||
|
internal const string BiometricIntegritySourceKey = "biometricIntegritySource";
|
||||||
|
internal const string iOSAutoFillBiometricIntegritySourceKey = "iOSAutoFillBiometricIntegritySource";
|
||||||
|
internal const string iOSExtensionBiometricIntegritySourceKey = "iOSExtensionBiometricIntegritySource";
|
||||||
|
|
||||||
|
internal const string iOSShareExtensionBiometricIntegritySourceKey =
|
||||||
|
"iOSShareExtensionBiometricIntegritySource";
|
||||||
|
|
||||||
|
internal static string AccountBiometricIntegrityValidKey(string userId, string systemBioIntegrityState) =>
|
||||||
|
$"accountBiometricIntegrityValid_{userId}_{systemBioIntegrityState}";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
@ -272,6 +272,36 @@ namespace Bit.Core.Services
|
||||||
await SaveAccountAsync(account, reconciledOptions);
|
await SaveAccountAsync(account, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetSystemBiometricIntegrityState(string bioIntegritySrcKey)
|
||||||
|
{
|
||||||
|
return await GetValueAsync<string>(bioIntegritySrcKey, await GetDefaultStorageOptionsAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetSystemBiometricIntegrityState(string bioIntegritySrcKey, string systemBioIntegrityState)
|
||||||
|
{
|
||||||
|
await SetValueAsync(bioIntegritySrcKey, systemBioIntegrityState, await GetDefaultStorageOptionsAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null)
|
||||||
|
{
|
||||||
|
var systemBioIntegrityState = await GetSystemBiometricIntegrityState(bioIntegritySrcKey);
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultStorageOptionsAsync());
|
||||||
|
return await GetValueAsync<bool?>(
|
||||||
|
Constants.AccountBiometricIntegrityValidKey(reconciledOptions.UserId, systemBioIntegrityState),
|
||||||
|
reconciledOptions) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null)
|
||||||
|
{
|
||||||
|
var systemBioIntegrityState = await GetSystemBiometricIntegrityState(bioIntegritySrcKey);
|
||||||
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
|
await GetDefaultStorageOptionsAsync());
|
||||||
|
await SetValueAsync(
|
||||||
|
Constants.AccountBiometricIntegrityValidKey(reconciledOptions.UserId, systemBioIntegrityState),
|
||||||
|
true, reconciledOptions);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
||||||
{
|
{
|
||||||
if (userId == null)
|
if (userId == null)
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Bit.iOS.Autofill
|
||||||
public LockPasswordViewController(IntPtr handle)
|
public LockPasswordViewController(IntPtr handle)
|
||||||
: base(handle)
|
: base(handle)
|
||||||
{
|
{
|
||||||
BiometricIntegrityKey = Bit.Core.Constants.iOSAutoFillBiometricIntegrityKey;
|
BiometricIntegritySourceKey = Bit.Core.Constants.iOSAutoFillBiometricIntegritySourceKey;
|
||||||
DismissModalAction = Cancel;
|
DismissModalAction = Cancel;
|
||||||
autofillExtension = true;
|
autofillExtension = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell(
|
public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell(
|
||||||
AppResources.MasterPassword, buttonsConfig: FormEntryTableViewCell.ButtonsConfig.One);
|
AppResources.MasterPassword, buttonsConfig: FormEntryTableViewCell.ButtonsConfig.One);
|
||||||
|
|
||||||
public string BiometricIntegrityKey { get; set; }
|
public string BiometricIntegritySourceKey { get; set; }
|
||||||
|
|
||||||
public UITableViewCell BiometricCell
|
public UITableViewCell BiometricCell
|
||||||
{
|
{
|
||||||
|
@ -77,7 +77,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
|
cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
|
||||||
cell.TextLabel.Lines = 0;
|
cell.TextLabel.Lines = 0;
|
||||||
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
|
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
|
||||||
cell.TextLabel.Text = AppResources.BiometricInvalidatedExtension;
|
cell.TextLabel.Text = AppResources.AccountBiometricInvalidatedExtension;
|
||||||
}
|
}
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,8 @@ namespace Bit.iOS.Core.Controllers
|
||||||
_isPinProtectedWithKey;
|
_isPinProtectedWithKey;
|
||||||
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
|
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
|
||||||
await _cryptoService.HasKeyAsync();
|
await _cryptoService.HasKeyAsync();
|
||||||
_biometricIntegrityValid = await _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey);
|
_biometricIntegrityValid =
|
||||||
|
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
||||||
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
||||||
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
|
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
|
||||||
}
|
}
|
||||||
|
@ -371,7 +372,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
// Re-enable biometrics if initial use
|
// Re-enable biometrics if initial use
|
||||||
if (_biometricLock & !_biometricIntegrityValid)
|
if (_biometricLock & !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync(BiometricIntegrityKey);
|
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell(
|
public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell(
|
||||||
AppResources.MasterPassword, buttonsConfig: FormEntryTableViewCell.ButtonsConfig.One);
|
AppResources.MasterPassword, buttonsConfig: FormEntryTableViewCell.ButtonsConfig.One);
|
||||||
|
|
||||||
public string BiometricIntegrityKey { get; set; }
|
public string BiometricIntegritySourceKey { get; set; }
|
||||||
|
|
||||||
public UITableViewCell BiometricCell
|
public UITableViewCell BiometricCell
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
|
cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
|
||||||
cell.TextLabel.Lines = 0;
|
cell.TextLabel.Lines = 0;
|
||||||
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
|
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
|
||||||
cell.TextLabel.Text = AppResources.BiometricInvalidatedExtension;
|
cell.TextLabel.Text = AppResources.AccountBiometricInvalidatedExtension;
|
||||||
}
|
}
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,8 @@ namespace Bit.iOS.Core.Controllers
|
||||||
_isPinProtectedWithKey;
|
_isPinProtectedWithKey;
|
||||||
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
|
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
|
||||||
await _cryptoService.HasKeyAsync();
|
await _cryptoService.HasKeyAsync();
|
||||||
_biometricIntegrityValid = await _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey);
|
_biometricIntegrityValid =
|
||||||
|
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
||||||
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
||||||
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
|
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
|
||||||
}
|
}
|
||||||
|
@ -361,7 +362,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
// Re-enable biometrics if initial use
|
// Re-enable biometrics if initial use
|
||||||
if (_biometricLock & !_biometricIntegrityValid)
|
if (_biometricLock & !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync(BiometricIntegrityKey);
|
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,29 +7,30 @@ namespace Bit.iOS.Core.Services
|
||||||
{
|
{
|
||||||
public class BiometricService : IBiometricService
|
public class BiometricService : IBiometricService
|
||||||
{
|
{
|
||||||
private IStorageService _storageService;
|
private IStateService _stateService;
|
||||||
|
|
||||||
public BiometricService(IStorageService storageService)
|
public BiometricService(IStateService stateService)
|
||||||
{
|
{
|
||||||
_storageService = storageService;
|
_stateService = stateService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SetupBiometricAsync(string bioIntegrityKey = null)
|
public async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
if (bioIntegrityKey == null)
|
if (bioIntegritySrcKey == null)
|
||||||
{
|
{
|
||||||
bioIntegrityKey = Bit.Core.Constants.BiometricIntegrityKey;
|
bioIntegritySrcKey = Bit.Core.Constants.BiometricIntegritySourceKey;
|
||||||
}
|
}
|
||||||
var state = GetState();
|
var state = GetState();
|
||||||
if (state != null)
|
if (state != null)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(bioIntegrityKey, ToBase64(state));
|
await _stateService.SetSystemBiometricIntegrityState(bioIntegritySrcKey, ToBase64(state));
|
||||||
|
await _stateService.SetAccountBiometricIntegrityValidAsync(bioIntegritySrcKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null)
|
public async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
|
||||||
{
|
{
|
||||||
var state = GetState();
|
var state = GetState();
|
||||||
if (state == null)
|
if (state == null)
|
||||||
|
@ -38,18 +39,14 @@ namespace Bit.iOS.Core.Services
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bioIntegrityKey == null)
|
if (bioIntegritySrcKey == null)
|
||||||
{
|
{
|
||||||
bioIntegrityKey = Bit.Core.Constants.BiometricIntegrityKey;
|
bioIntegritySrcKey = Bit.Core.Constants.BiometricIntegritySourceKey;
|
||||||
}
|
}
|
||||||
var oldState = await _storageService.GetAsync<string>(bioIntegrityKey);
|
var savedState = await _stateService.GetSystemBiometricIntegrityState(bioIntegritySrcKey);
|
||||||
if (oldState == null)
|
if (savedState != null)
|
||||||
{
|
{
|
||||||
oldState = await GetMigratedIntegrityState(bioIntegrityKey);
|
return FromBase64(savedState).Equals(state);
|
||||||
}
|
|
||||||
if (oldState != null)
|
|
||||||
{
|
|
||||||
return FromBase64(oldState).Equals(state);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -72,35 +69,5 @@ namespace Bit.iOS.Core.Services
|
||||||
var bytes = System.Convert.FromBase64String(data);
|
var bytes = System.Convert.FromBase64String(data);
|
||||||
return NSData.FromArray(bytes);
|
return NSData.FromArray(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GetMigratedIntegrityState(string bioIntegrityKey)
|
|
||||||
{
|
|
||||||
var legacyKey = "biometricState";
|
|
||||||
if (bioIntegrityKey == Bit.Core.Constants.iOSAutoFillBiometricIntegrityKey)
|
|
||||||
{
|
|
||||||
legacyKey = "autofillBiometricState";
|
|
||||||
}
|
|
||||||
else if (bioIntegrityKey == Bit.Core.Constants.iOSExtensionBiometricIntegrityKey)
|
|
||||||
{
|
|
||||||
legacyKey = "extensionBiometricState";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Original values are pulled from DB since the legacy keys were never defined in _preferenceStorageKeys
|
|
||||||
var integrityState = await _storageService.GetAsync<string>(legacyKey);
|
|
||||||
if (integrityState != null)
|
|
||||||
{
|
|
||||||
// Save original value to pref storage with new key
|
|
||||||
await _storageService.SaveAsync(bioIntegrityKey, integrityState);
|
|
||||||
|
|
||||||
// Remove value from DB storage with legacy key
|
|
||||||
await _storageService.RemoveAsync(legacyKey);
|
|
||||||
|
|
||||||
// Return value as if it was always in pref storage
|
|
||||||
return integrityState;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return null since the state was never set
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ namespace Bit.iOS.Core.Utilities
|
||||||
}
|
}
|
||||||
|
|
||||||
var avatarImageSource = new AvatarImageSource(await _stateService.GetActiveUserIdAsync(),
|
var avatarImageSource = new AvatarImageSource(await _stateService.GetActiveUserIdAsync(),
|
||||||
await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
|
await _stateService.GetNameAsync(), await _stateService.GetEmailAsync(),
|
||||||
|
await _stateService.GetAvatarColorAsync());
|
||||||
using (var avatarUIImage = await avatarImageSource.GetNativeImageAsync())
|
using (var avatarUIImage = await avatarImageSource.GetNativeImageAsync())
|
||||||
{
|
{
|
||||||
return avatarUIImage?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) ?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
|
return avatarUIImage?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) ?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Bit.App.Services;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.App.Utilities.AccountManagement;
|
using Bit.App.Utilities.AccountManagement;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.iOS.Core.Services;
|
using Bit.iOS.Core.Services;
|
||||||
|
@ -105,13 +106,13 @@ namespace Bit.iOS.Core.Utilities
|
||||||
var storageMediatorService = new StorageMediatorService(mobileStorageService, secureStorageService, preferencesStorage);
|
var storageMediatorService = new StorageMediatorService(mobileStorageService, secureStorageService, preferencesStorage);
|
||||||
var stateService = new StateService(mobileStorageService, secureStorageService, storageMediatorService, messagingService);
|
var stateService = new StateService(mobileStorageService, secureStorageService, storageMediatorService, messagingService);
|
||||||
var stateMigrationService =
|
var stateMigrationService =
|
||||||
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
new StateMigrationService(DeviceType.iOS, liteDbStorage, preferencesStorage, secureStorageService);
|
||||||
var deviceActionService = new DeviceActionService();
|
var deviceActionService = new DeviceActionService();
|
||||||
var fileService = new FileService(stateService, messagingService);
|
var fileService = new FileService(stateService, messagingService);
|
||||||
var clipboardService = new ClipboardService(stateService);
|
var clipboardService = new ClipboardService(stateService);
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
||||||
messagingService, broadcasterService);
|
messagingService, broadcasterService);
|
||||||
var biometricService = new BiometricService(mobileStorageService);
|
var biometricService = new BiometricService(stateService);
|
||||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace Bit.iOS.Extension
|
||||||
public LockPasswordViewController(IntPtr handle)
|
public LockPasswordViewController(IntPtr handle)
|
||||||
: base(handle)
|
: base(handle)
|
||||||
{
|
{
|
||||||
BiometricIntegrityKey = Bit.Core.Constants.iOSExtensionBiometricIntegrityKey;
|
BiometricIntegritySourceKey = Bit.Core.Constants.iOSExtensionBiometricIntegritySourceKey;
|
||||||
DismissModalAction = Cancel;
|
DismissModalAction = Cancel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,14 @@ namespace Bit.iOS.ShareExtension
|
||||||
|
|
||||||
public LockPasswordViewController()
|
public LockPasswordViewController()
|
||||||
{
|
{
|
||||||
BiometricIntegrityKey = Bit.Core.Constants.iOSShareExtensionBiometricIntegrityKey;
|
BiometricIntegritySourceKey = Bit.Core.Constants.iOSShareExtensionBiometricIntegritySourceKey;
|
||||||
DismissModalAction = Cancel;
|
DismissModalAction = Cancel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LockPasswordViewController(IntPtr handle)
|
public LockPasswordViewController(IntPtr handle)
|
||||||
: base(handle)
|
: base(handle)
|
||||||
{
|
{
|
||||||
BiometricIntegrityKey = Bit.Core.Constants.iOSShareExtensionBiometricIntegrityKey;
|
BiometricIntegritySourceKey = Bit.Core.Constants.iOSShareExtensionBiometricIntegritySourceKey;
|
||||||
DismissModalAction = Cancel;
|
DismissModalAction = Cancel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue