1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-12-27 17:08:00 +01:00

biometric integrity check in iOS extensions (#1093)

This commit is contained in:
Matt Portune 2020-09-25 21:14:10 -04:00 committed by GitHub
parent 0387d5bdd1
commit 7b358b1bbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 16 deletions

View File

@ -111,7 +111,7 @@
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"
IsVisible="{Binding BiometricLock}" IsEnabled="{Binding BiometricIntegrityValid}"></Button> IsVisible="{Binding BiometricButtonVisible}"></Button>
<Button Text="{u:I18n LogOut}" Clicked="LogOut_Clicked"></Button> <Button Text="{u:I18n LogOut}" Clicked="LogOut_Clicked"></Button>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>

View File

@ -33,6 +33,7 @@ namespace Bit.App.Pages
private bool _pinLock; private bool _pinLock;
private bool _biometricLock; private bool _biometricLock;
private bool _biometricIntegrityValid = true; private bool _biometricIntegrityValid = true;
private bool _biometricButtonVisible = true;
private string _biometricButtonText; private string _biometricButtonText;
private string _loggedInAsText; private string _loggedInAsText;
private string _lockedVerifyText; private string _lockedVerifyText;
@ -87,6 +88,12 @@ namespace Bit.App.Pages
set => SetProperty(ref _biometricIntegrityValid, value); set => SetProperty(ref _biometricIntegrityValid, value);
} }
public bool BiometricButtonVisible
{
get => _biometricButtonVisible;
set => SetProperty(ref _biometricButtonVisible, value);
}
public string BiometricButtonText public string BiometricButtonText
{ {
get => _biometricButtonText; get => _biometricButtonText;
@ -138,6 +145,13 @@ namespace Bit.App.Pages
if (BiometricLock) if (BiometricLock)
{ {
BiometricIntegrityValid = await _biometricService.ValidateIntegrityAsync();
if (!_biometricIntegrityValid)
{
BiometricButtonVisible = false;
return;
}
BiometricButtonVisible = true;
BiometricButtonText = AppResources.UseBiometricsToUnlock; BiometricButtonText = AppResources.UseBiometricsToUnlock;
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
@ -145,8 +159,7 @@ namespace Bit.App.Pages
BiometricButtonText = supportsFace ? AppResources.UseFaceIDToUnlock : BiometricButtonText = supportsFace ? AppResources.UseFaceIDToUnlock :
AppResources.UseFingerprintToUnlock; AppResources.UseFingerprintToUnlock;
} }
BiometricIntegrityValid = await _biometricService.ValidateIntegrityAsync(); if (autoPromptBiometric)
if (autoPromptBiometric & _biometricIntegrityValid)
{ {
var tasks = Task.Run(async () => var tasks = Task.Run(async () =>
{ {

View File

@ -22,9 +22,11 @@ namespace Bit.iOS.Core.Controllers
private IStorageService _storageService; private IStorageService _storageService;
private IStorageService _secureStorageService; private IStorageService _secureStorageService;
private IPlatformUtilsService _platformUtilsService; private IPlatformUtilsService _platformUtilsService;
private IBiometricService _biometricService;
private Tuple<bool, bool> _pinSet; private Tuple<bool, bool> _pinSet;
private bool _pinLock; private bool _pinLock;
private bool _fingerprintLock; private bool _biometricLock;
private bool _biometricIntegrityValid = true;
private int _invalidPinAttempts; private int _invalidPinAttempts;
public LockPasswordViewController(IntPtr handle) public LockPasswordViewController(IntPtr handle)
@ -49,10 +51,12 @@ namespace Bit.iOS.Core.Controllers
_storageService = ServiceContainer.Resolve<IStorageService>("storageService"); _storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService"); _secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"); _platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
_pinSet = _vaultTimeoutService.IsPinLockSetAsync().GetAwaiter().GetResult(); _pinSet = _vaultTimeoutService.IsPinLockSetAsync().GetAwaiter().GetResult();
_pinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2; _pinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
_fingerprintLock = _vaultTimeoutService.IsBiometricLockSetAsync().GetAwaiter().GetResult(); _biometricLock = _vaultTimeoutService.IsBiometricLockSetAsync().GetAwaiter().GetResult() &&
_cryptoService.HasKeyAsync().GetAwaiter().GetResult();
BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword; BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword;
BaseCancelButton.Title = AppResources.Cancel; BaseCancelButton.Title = AppResources.Cancel;
@ -80,12 +84,17 @@ namespace Bit.iOS.Core.Controllers
base.ViewDidLoad(); base.ViewDidLoad();
if (_fingerprintLock) if (_biometricLock)
{ {
_biometricIntegrityValid = _biometricService.ValidateIntegrityAsync().GetAwaiter().GetResult();
if (!_biometricIntegrityValid)
{
return;
}
var tasks = Task.Run(async () => var tasks = Task.Run(async () =>
{ {
await Task.Delay(500); await Task.Delay(500);
NSRunLoop.Main.BeginInvokeOnMainThread(async () => await PromptFingerprintAsync()); NSRunLoop.Main.BeginInvokeOnMainThread(async () => await PromptBiometricAsync());
}); });
} }
} }
@ -93,7 +102,7 @@ namespace Bit.iOS.Core.Controllers
public override void ViewDidAppear(bool animated) public override void ViewDidAppear(bool animated)
{ {
base.ViewDidAppear(animated); base.ViewDidAppear(animated);
if (!_fingerprintLock) if (!_biometricLock || !_biometricIntegrityValid)
{ {
MasterPasswordCell.TextField.BecomeFirstResponder(); MasterPasswordCell.TextField.BecomeFirstResponder();
} }
@ -210,9 +219,9 @@ namespace Bit.iOS.Core.Controllers
Success(); Success();
} }
public async Task PromptFingerprintAsync() public async Task PromptBiometricAsync()
{ {
if (!_fingerprintLock) if (!_biometricLock || !_biometricIntegrityValid)
{ {
return; return;
} }
@ -260,12 +269,23 @@ namespace Bit.iOS.Core.Controllers
else if (indexPath.Section == 1) else if (indexPath.Section == 1)
{ {
if (indexPath.Row == 0) if (indexPath.Row == 0)
{
var cell = new ExtendedUITableViewCell();
if (_controller._biometricIntegrityValid)
{ {
var biometricButtonText = _controller._deviceActionService.SupportsFaceBiometric() ? var biometricButtonText = _controller._deviceActionService.SupportsFaceBiometric() ?
AppResources.UseFaceIDToUnlock : AppResources.UseFingerprintToUnlock; AppResources.UseFaceIDToUnlock : AppResources.UseFingerprintToUnlock;
var cell = new ExtendedUITableViewCell();
cell.TextLabel.TextColor = ThemeHelpers.PrimaryColor; cell.TextLabel.TextColor = ThemeHelpers.PrimaryColor;
cell.TextLabel.Text = biometricButtonText; cell.TextLabel.Text = biometricButtonText;
}
else
{
cell.TextLabel.TextColor = ThemeHelpers.DangerColor;
cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
cell.TextLabel.Lines = 0;
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
cell.TextLabel.Text = AppResources.BiometricInvalidated;
}
return cell; return cell;
} }
} }
@ -279,7 +299,7 @@ namespace Bit.iOS.Core.Controllers
public override nint NumberOfSections(UITableView tableView) public override nint NumberOfSections(UITableView tableView)
{ {
return _controller._fingerprintLock ? 2 : 1; return _controller._biometricLock ? 2 : 1;
} }
public override nint RowsInSection(UITableView tableview, nint section) public override nint RowsInSection(UITableView tableview, nint section)
@ -307,7 +327,7 @@ namespace Bit.iOS.Core.Controllers
tableView.EndEditing(true); tableView.EndEditing(true);
if (indexPath.Section == 1 && indexPath.Row == 0) if (indexPath.Section == 1 && indexPath.Row == 0)
{ {
var task = _controller.PromptFingerprintAsync(); var task = _controller.PromptBiometricAsync();
return; return;
} }
var cell = tableView.CellAt(indexPath); var cell = tableView.CellAt(indexPath);

View File

@ -11,6 +11,7 @@ namespace Bit.iOS.Core.Utilities
public static UIColor BackgroundColor = Xamarin.Forms.Color.FromHex("#ffffff").ToUIColor(); public static UIColor BackgroundColor = Xamarin.Forms.Color.FromHex("#ffffff").ToUIColor();
public static UIColor MutedColor = Xamarin.Forms.Color.FromHex("#777777").ToUIColor(); public static UIColor MutedColor = Xamarin.Forms.Color.FromHex("#777777").ToUIColor();
public static UIColor SuccessColor = Xamarin.Forms.Color.FromHex("#00a65a").ToUIColor(); public static UIColor SuccessColor = Xamarin.Forms.Color.FromHex("#00a65a").ToUIColor();
public static UIColor DangerColor = Xamarin.Forms.Color.FromHex("dd4b39").ToUIColor();
public static UIColor PrimaryColor = Xamarin.Forms.Color.FromHex("#175DDC").ToUIColor(); public static UIColor PrimaryColor = Xamarin.Forms.Color.FromHex("#175DDC").ToUIColor();
public static UIColor TextColor = Xamarin.Forms.Color.FromHex("#000000").ToUIColor(); public static UIColor TextColor = Xamarin.Forms.Color.FromHex("#000000").ToUIColor();
public static UIColor PlaceholderColor = Xamarin.Forms.Color.FromHex("#d0d0d0").ToUIColor(); public static UIColor PlaceholderColor = Xamarin.Forms.Color.FromHex("#d0d0d0").ToUIColor();
@ -56,6 +57,12 @@ namespace Bit.iOS.Core.Utilities
UILabel.AppearanceWhenContainedIn(typeof(UITableViewHeaderFooterView)).TextColor = MutedColor; UILabel.AppearanceWhenContainedIn(typeof(UITableViewHeaderFooterView)).TextColor = MutedColor;
} }
public static UIFont GetDangerFont()
{
return Xamarin.Forms.Font.SystemFontOfSize(Xamarin.Forms.NamedSize.Small,
Xamarin.Forms.FontAttributes.Bold).ToUIFont();
}
private static void SetThemeVariables(string theme) private static void SetThemeVariables(string theme)
{ {
LightTheme = false; LightTheme = false;
@ -69,6 +76,7 @@ namespace Bit.iOS.Core.Utilities
var whiteColor = Xamarin.Forms.Color.FromHex("#ffffff").ToUIColor(); var whiteColor = Xamarin.Forms.Color.FromHex("#ffffff").ToUIColor();
MutedColor = Xamarin.Forms.Color.FromHex("#a3a3a3").ToUIColor(); MutedColor = Xamarin.Forms.Color.FromHex("#a3a3a3").ToUIColor();
SuccessColor = Xamarin.Forms.Color.FromHex("#00a65a").ToUIColor(); SuccessColor = Xamarin.Forms.Color.FromHex("#00a65a").ToUIColor();
DangerColor = Xamarin.Forms.Color.FromHex("ff3e24").ToUIColor();
BackgroundColor = Xamarin.Forms.Color.FromHex("#303030").ToUIColor(); BackgroundColor = Xamarin.Forms.Color.FromHex("#303030").ToUIColor();
SplashBackgroundColor = Xamarin.Forms.Color.FromHex("#222222").ToUIColor(); SplashBackgroundColor = Xamarin.Forms.Color.FromHex("#222222").ToUIColor();
PrimaryColor = Xamarin.Forms.Color.FromHex("#52bdfb").ToUIColor(); PrimaryColor = Xamarin.Forms.Color.FromHex("#52bdfb").ToUIColor();
@ -85,6 +93,7 @@ namespace Bit.iOS.Core.Utilities
var whiteColor = Xamarin.Forms.Color.FromHex("#ffffff").ToUIColor(); var whiteColor = Xamarin.Forms.Color.FromHex("#ffffff").ToUIColor();
MutedColor = Xamarin.Forms.Color.FromHex("#a3a3a3").ToUIColor(); MutedColor = Xamarin.Forms.Color.FromHex("#a3a3a3").ToUIColor();
SuccessColor = Xamarin.Forms.Color.FromHex("#00a65a").ToUIColor(); SuccessColor = Xamarin.Forms.Color.FromHex("#00a65a").ToUIColor();
DangerColor = Xamarin.Forms.Color.FromHex("ff3e24").ToUIColor();
BackgroundColor = blackColor; BackgroundColor = blackColor;
SplashBackgroundColor = blackColor; SplashBackgroundColor = blackColor;
PrimaryColor = Xamarin.Forms.Color.FromHex("#52bdfb").ToUIColor(); PrimaryColor = Xamarin.Forms.Color.FromHex("#52bdfb").ToUIColor();
@ -99,6 +108,7 @@ namespace Bit.iOS.Core.Utilities
{ {
MutedColor = Xamarin.Forms.Color.FromHex("#d8dee9").ToUIColor(); MutedColor = Xamarin.Forms.Color.FromHex("#d8dee9").ToUIColor();
SuccessColor = Xamarin.Forms.Color.FromHex("#a3be8c").ToUIColor(); SuccessColor = Xamarin.Forms.Color.FromHex("#a3be8c").ToUIColor();
DangerColor = Xamarin.Forms.Color.FromHex("bf616a").ToUIColor();
BackgroundColor = Xamarin.Forms.Color.FromHex("#3b4252").ToUIColor(); BackgroundColor = Xamarin.Forms.Color.FromHex("#3b4252").ToUIColor();
SplashBackgroundColor = Xamarin.Forms.Color.FromHex("#2e3440").ToUIColor(); SplashBackgroundColor = Xamarin.Forms.Color.FromHex("#2e3440").ToUIColor();
PrimaryColor = Xamarin.Forms.Color.FromHex("#81a1c1").ToUIColor(); PrimaryColor = Xamarin.Forms.Color.FromHex("#81a1c1").ToUIColor();