From 5761b47073c1792598a32a52b0176ded3c62c670 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 16 May 2019 17:30:07 -0400 Subject: [PATCH] fingerprint locking --- src/App/App.xaml.cs | 1 + src/App/Pages/Accounts/LockPage.xaml.cs | 2 +- src/App/Pages/Accounts/LockPageViewModel.cs | 24 +++++++++++++++ src/App/Pages/Settings/SettingsPage.xaml.cs | 5 ++++ .../Pages/Settings/SettingsPageViewModel.cs | 16 +++++++++- .../Services/MobilePlatformUtilsService.cs | 10 ++----- src/Core/Abstractions/ILockService.cs | 2 ++ .../Abstractions/IPlatformUtilsService.cs | 2 +- src/Core/Constants.cs | 1 + src/Core/Services/CryptoService.cs | 6 ++-- src/Core/Services/LockService.cs | 29 +++++++++++++++++-- 11 files changed, 84 insertions(+), 14 deletions(-) diff --git a/src/App/App.xaml.cs b/src/App/App.xaml.cs index 2d2d3280a..f1bb631a8 100644 --- a/src/App/App.xaml.cs +++ b/src/App/App.xaml.cs @@ -147,6 +147,7 @@ namespace Bit.App _passwordGenerationService.ClearAsync(), _lockService.ClearAsync()); _lockService.PinLocked = false; + _lockService.FingerprintLocked = true; _searchService.ClearIndex(); _authService.LogOut(() => { diff --git a/src/App/Pages/Accounts/LockPage.xaml.cs b/src/App/Pages/Accounts/LockPage.xaml.cs index bf605b36c..a04093f4e 100644 --- a/src/App/Pages/Accounts/LockPage.xaml.cs +++ b/src/App/Pages/Accounts/LockPage.xaml.cs @@ -28,7 +28,7 @@ namespace Bit.App.Pages { RequestFocus(PinEntry); } - else + else if(!_vm.FingerprintLock) { RequestFocus(MasterPasswordEntry); } diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 5bae3af41..aab615a86 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -24,6 +24,7 @@ namespace Bit.App.Pages private string _email; private bool _showPassword; private bool _pinLock; + private bool _fingerprintLock; private int _invalidPinAttempts = 0; private Tuple _pinSet; @@ -57,6 +58,12 @@ namespace Bit.App.Pages set => SetProperty(ref _pinLock, value); } + public bool FingerprintLock + { + get => _fingerprintLock; + set => SetProperty(ref _fingerprintLock, value); + } + public Command TogglePasswordCommand { get; } public string ShowPasswordIcon => ShowPassword ? "" : ""; public string MasterPassword { get; set; } @@ -67,8 +74,25 @@ namespace Bit.App.Pages _pinSet = await _lockService.IsPinLockSetAsync(); var hasKey = await _cryptoService.HasKeyAsync(); PinLock = (_pinSet.Item1 && hasKey) || _pinSet.Item2; + FingerprintLock = await _lockService.IsFingerprintLockSetAsync(); _email = await _userService.GetEmailAsync(); PageTitle = PinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword; + + if(FingerprintLock) + { + var tasks = Task.Run(async () => + { + await Task.Delay(500); + Device.BeginInvokeOnMainThread(async () => { + var success = await _platformUtilsService.AuthenticateFingerprintAsync(); + _lockService.FingerprintLocked = !success; + if(success) + { + DoContinue(); + } + }); + }); + } } public async Task SubmitAsync() diff --git a/src/App/Pages/Settings/SettingsPage.xaml.cs b/src/App/Pages/Settings/SettingsPage.xaml.cs index b5d3c3d87..faac65c81 100644 --- a/src/App/Pages/Settings/SettingsPage.xaml.cs +++ b/src/App/Pages/Settings/SettingsPage.xaml.cs @@ -102,6 +102,11 @@ namespace Bit.App.Pages { await _vm.UpdatePinAsync(); } + else if(item.Name.Contains(AppResources.Fingerprint) || item.Name.Contains(AppResources.TouchID) || + item.Name.Contains(AppResources.FaceID)) + { + await _vm.UpdateFingerprintAsync(); + } } } } diff --git a/src/App/Pages/Settings/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPageViewModel.cs index f0fac9676..2165ee938 100644 --- a/src/App/Pages/Settings/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPageViewModel.cs @@ -80,7 +80,7 @@ namespace Bit.App.Pages _lockOptionValue = _lockOptions.FirstOrDefault(o => o.Value == option).Key; var pinSet = await _lockService.IsPinLockSetAsync(); _pin = pinSet.Item1 || pinSet.Item2; - // TODO: Fingerprint + _fingerprint = await _lockService.IsFingerprintLockSetAsync(); BuildList(); } @@ -238,7 +238,21 @@ namespace Bit.App.Pages await _storageService.RemoveAsync(Constants.PinProtectedKey); await _storageService.RemoveAsync(Constants.ProtectedPin); } + BuildList(); + } + public async Task UpdateFingerprintAsync() + { + _fingerprint = !_fingerprint; + if(_fingerprint) + { + await _storageService.SaveAsync(Constants.FingerprintUnlockKey, true); + } + else + { + await _storageService.RemoveAsync(Constants.FingerprintUnlockKey); + } + await _cryptoService.ToggleKeyAsync(); BuildList(); } diff --git a/src/App/Services/MobilePlatformUtilsService.cs b/src/App/Services/MobilePlatformUtilsService.cs index a35fa3347..4e244df55 100644 --- a/src/App/Services/MobilePlatformUtilsService.cs +++ b/src/App/Services/MobilePlatformUtilsService.cs @@ -200,29 +200,25 @@ namespace Bit.App.Services } } - public async Task AuthenticateFingerprintAsync(string text = null, Action fallback = null) + public async Task AuthenticateFingerprintAsync(string text = null) { try { if(text == null) { text = AppResources.FingerprintDirection; + // TODO: face id direction } var fingerprintRequest = new AuthenticationRequestConfiguration(text) { AllowAlternativeAuthentication = true, - CancelTitle = AppResources.Cancel, - FallbackTitle = AppResources.LogOut + CancelTitle = AppResources.Cancel }; var result = await CrossFingerprint.Current.AuthenticateAsync(fingerprintRequest); if(result.Authenticated) { return true; } - else if(result.Status == FingerprintAuthenticationResultStatus.FallbackRequested) - { - fallback?.Invoke(); - } } catch { } return false; diff --git a/src/Core/Abstractions/ILockService.cs b/src/Core/Abstractions/ILockService.cs index 2504f4d43..d6c167f85 100644 --- a/src/Core/Abstractions/ILockService.cs +++ b/src/Core/Abstractions/ILockService.cs @@ -6,11 +6,13 @@ namespace Bit.Core.Abstractions public interface ILockService { bool PinLocked { get; set; } + bool FingerprintLocked { get; set; } Task CheckLockAsync(); Task ClearAsync(); Task IsLockedAsync(); Task> IsPinLockSetAsync(); + Task IsFingerprintLockSetAsync(); Task LockAsync(bool allowSoftLock = false); Task SetLockOptionAsync(int? lockOption); } diff --git a/src/Core/Abstractions/IPlatformUtilsService.cs b/src/Core/Abstractions/IPlatformUtilsService.cs index 10a6f9bb0..f7b545037 100644 --- a/src/Core/Abstractions/IPlatformUtilsService.cs +++ b/src/Core/Abstractions/IPlatformUtilsService.cs @@ -27,6 +27,6 @@ namespace Bit.Core.Abstractions bool SupportsU2f(); bool SupportsDuo(); Task SupportsFingerprintAsync(); - Task AuthenticateFingerprintAsync(string text = null, Action fallback = null); + Task AuthenticateFingerprintAsync(string text = null); } } \ No newline at end of file diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 6f6c0af98..848658a6e 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -6,6 +6,7 @@ public const string iOSAppProtocol = "iosapp://"; public static string LockOptionKey = "lockOption"; public static string LastActiveKey = "lastActive"; + public static string FingerprintUnlockKey = "fingerprintUnlock"; public static string ProtectedPin = "protectedPin"; public static string PinProtectedKey = "pinProtectedKey"; public static string DefaultUriMatch = "defaultUriMatch"; diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 6b889a84f..4f546c955 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -47,7 +47,8 @@ namespace Bit.Core.Services { _key = key; var option = await _storageService.GetAsync(Constants.LockOptionKey); - if(option.HasValue) + var fingerprint = await _storageService.GetAsync(Constants.FingerprintUnlockKey); + if(option.HasValue && !fingerprint.GetValueOrDefault()) { // If we have a lock option set, we do not store the key return; @@ -349,7 +350,8 @@ namespace Bit.Core.Services { var key = await GetKeyAsync(); var option = await _storageService.GetAsync(Constants.LockOptionKey); - if(option != null || option == 0) + var fingerprint = await _storageService.GetAsync(Constants.FingerprintUnlockKey); + if(!fingerprint.GetValueOrDefault() && (option != null || option == 0)) { await ClearKeyAsync(); _key = key; diff --git a/src/Core/Services/LockService.cs b/src/Core/Services/LockService.cs index e9adfd709..209598a2d 100644 --- a/src/Core/Services/LockService.cs +++ b/src/Core/Services/LockService.cs @@ -39,15 +39,27 @@ namespace Bit.Core.Services } public bool PinLocked { get; set; } + public bool FingerprintLocked { get; set; } = true; // TODO: init timer? public async Task IsLockedAsync() { var hasKey = await _cryptoService.HasKeyAsync(); - if(hasKey && PinLocked) + if(hasKey) { - return true; + if(PinLocked) + { + return true; + } + else + { + var fingerprintSet = await IsFingerprintLockSetAsync(); + if(fingerprintSet && FingerprintLocked) + { + return true; + } + } } return !hasKey; } @@ -102,6 +114,13 @@ namespace Bit.Core.Services if(pinSet.Item1) { PinLocked = true; + } + if(await IsFingerprintLockSetAsync()) + { + FingerprintLocked = true; + } + if(FingerprintLocked || PinLocked) + { _messagingService.Send("locked"); // TODO: locked callback? return; @@ -134,6 +153,12 @@ namespace Bit.Core.Services return new Tuple(protectedPin != null, pinProtectedKey != null); } + public async Task IsFingerprintLockSetAsync() + { + var fingerprintLock = await _storageService.GetAsync(Constants.FingerprintUnlockKey); + return fingerprintLock.GetValueOrDefault(); + } + public async Task ClearAsync() { await _storageService.RemoveAsync(Constants.ProtectedPin);