1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-24 04:04:34 +00: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:
mp-bw
2023-05-01 09:47:00 -04:00
committed by GitHub
parent 4f0238122b
commit 0f417b8434
22 changed files with 236 additions and 102 deletions

View File

@@ -56,7 +56,7 @@ namespace Bit.iOS.Core.Controllers
public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell(
AppResources.MasterPassword, buttonsConfig: FormEntryTableViewCell.ButtonsConfig.One);
public string BiometricIntegrityKey { get; set; }
public string BiometricIntegritySourceKey { get; set; }
public UITableViewCell BiometricCell
{
@@ -77,7 +77,7 @@ namespace Bit.iOS.Core.Controllers
cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
cell.TextLabel.Lines = 0;
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
cell.TextLabel.Text = AppResources.BiometricInvalidatedExtension;
cell.TextLabel.Text = AppResources.AccountBiometricInvalidatedExtension;
}
return cell;
}
@@ -114,7 +114,8 @@ namespace Bit.iOS.Core.Controllers
_isPinProtectedWithKey;
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
await _cryptoService.HasKeyAsync();
_biometricIntegrityValid = await _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey);
_biometricIntegrityValid =
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
}
@@ -371,7 +372,7 @@ namespace Bit.iOS.Core.Controllers
// Re-enable biometrics if initial use
if (_biometricLock & !_biometricIntegrityValid)
{
await _biometricService.SetupBiometricAsync(BiometricIntegrityKey);
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
}
}

View File

@@ -53,7 +53,7 @@ namespace Bit.iOS.Core.Controllers
public FormEntryTableViewCell MasterPasswordCell { get; set; } = new FormEntryTableViewCell(
AppResources.MasterPassword, buttonsConfig: FormEntryTableViewCell.ButtonsConfig.One);
public string BiometricIntegrityKey { get; set; }
public string BiometricIntegritySourceKey { get; set; }
public UITableViewCell BiometricCell
{
@@ -74,7 +74,7 @@ namespace Bit.iOS.Core.Controllers
cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
cell.TextLabel.Lines = 0;
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
cell.TextLabel.Text = AppResources.BiometricInvalidatedExtension;
cell.TextLabel.Text = AppResources.AccountBiometricInvalidatedExtension;
}
return cell;
}
@@ -108,7 +108,8 @@ namespace Bit.iOS.Core.Controllers
_isPinProtectedWithKey;
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
await _cryptoService.HasKeyAsync();
_biometricIntegrityValid = await _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey);
_biometricIntegrityValid =
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
}
@@ -361,7 +362,7 @@ namespace Bit.iOS.Core.Controllers
// Re-enable biometrics if initial use
if (_biometricLock & !_biometricIntegrityValid)
{
await _biometricService.SetupBiometricAsync(BiometricIntegrityKey);
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
}
}

View File

@@ -7,29 +7,30 @@ namespace Bit.iOS.Core.Services
{
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();
if (state != null)
{
await _storageService.SaveAsync(bioIntegrityKey, ToBase64(state));
await _stateService.SetSystemBiometricIntegrityState(bioIntegritySrcKey, ToBase64(state));
await _stateService.SetAccountBiometricIntegrityValidAsync(bioIntegritySrcKey);
}
return true;
}
public async Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null)
public async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null)
{
var state = GetState();
if (state == null)
@@ -38,18 +39,14 @@ namespace Bit.iOS.Core.Services
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);
if (oldState == null)
var savedState = await _stateService.GetSystemBiometricIntegrityState(bioIntegritySrcKey);
if (savedState != null)
{
oldState = await GetMigratedIntegrityState(bioIntegrityKey);
}
if (oldState != null)
{
return FromBase64(oldState).Equals(state);
return FromBase64(savedState).Equals(state);
}
return false;
}
@@ -72,35 +69,5 @@ namespace Bit.iOS.Core.Services
var bytes = System.Convert.FromBase64String(data);
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;
}
}
}

View File

@@ -34,7 +34,8 @@ namespace Bit.iOS.Core.Utilities
}
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())
{
return avatarUIImage?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) ?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);

View File

@@ -10,6 +10,7 @@ using Bit.App.Services;
using Bit.App.Utilities;
using Bit.App.Utilities.AccountManagement;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.iOS.Core.Services;
@@ -105,13 +106,13 @@ namespace Bit.iOS.Core.Utilities
var storageMediatorService = new StorageMediatorService(mobileStorageService, secureStorageService, preferencesStorage);
var stateService = new StateService(mobileStorageService, secureStorageService, storageMediatorService, messagingService);
var stateMigrationService =
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
new StateMigrationService(DeviceType.iOS, liteDbStorage, preferencesStorage, secureStorageService);
var deviceActionService = new DeviceActionService();
var fileService = new FileService(stateService, messagingService);
var clipboardService = new ClipboardService(stateService);
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
messagingService, broadcasterService);
var biometricService = new BiometricService(mobileStorageService);
var biometricService = new BiometricService(stateService);
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);