From ffd8f9951f9e00d7bed90d11ec4ad4fc621008d0 Mon Sep 17 00:00:00 2001 From: Matt Portune <59324545+mportune-bw@users.noreply.github.com> Date: Tue, 1 Dec 2020 15:30:23 -0500 Subject: [PATCH] Fix for missing biometric integrity check in iOS extensions under certain conditions (#1162) * Fix for biometric check in extension on fresh install * make sure bio integrity values are written to pref storage * integrity state migration to pref storage * remove automatic state saving upon null validation --- src/App/Resources/AppResources.Designer.cs | 6 ++ src/App/Resources/AppResources.resx | 5 +- src/App/Services/MobileStorageService.cs | 3 + src/Core/Constants.cs | 3 + .../LockPasswordViewController.cs | 2 +- .../Controllers/LockPasswordViewController.cs | 6 +- src/iOS.Core/Services/BiometricService.cs | 57 ++++++++++++++----- .../LockPasswordViewController.cs | 2 +- 8 files changed, 64 insertions(+), 20 deletions(-) diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index 12695127b..00bc055c6 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -2943,6 +2943,12 @@ namespace Bit.App.Resources { } } + public static string BiometricInvalidatedExtension { + get { + return ResourceManager.GetString("BiometricInvalidatedExtension", resourceCulture); + } + } + public static string EnableSyncOnRefresh { get { return ResourceManager.GetString("EnableSyncOnRefresh", resourceCulture); diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index 9ae1aae89..48de532c8 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -1675,7 +1675,10 @@ Confirmation alert message when soft-deleting a cipher. - Biometric change detected, login using Master Password to enable again. + Biometric unlock disabled pending verification of master password. + + + Biometric unlock for autofill disabled pending verification of master password. Enable sync on refresh diff --git a/src/App/Services/MobileStorageService.cs b/src/App/Services/MobileStorageService.cs index ba262dde9..610d340e0 100644 --- a/src/App/Services/MobileStorageService.cs +++ b/src/App/Services/MobileStorageService.cs @@ -32,8 +32,11 @@ namespace Bit.App.Services Constants.MigratedFromV1AutofillPromptShown, Constants.TriedV1Resync, Constants.ClearCiphersCacheKey, + Constants.BiometricIntegrityKey, Constants.iOSAutoFillClearCiphersCacheKey, + Constants.iOSAutoFillBiometricIntegrityKey, Constants.iOSExtensionClearCiphersCacheKey, + Constants.iOSExtensionBiometricIntegrityKey, Constants.EnvironmentUrlsKey, Constants.InlineAutofillEnabledKey, }; diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index b2b6164ea..a96dd2364 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -29,8 +29,11 @@ public static string OldUserIdKey = "userId"; public static string AddSitePromptShownKey = "addSitePromptShown"; public static string ClearCiphersCacheKey = "clearCiphersCache"; + public static string BiometricIntegrityKey = "biometricIntegrityState"; public static string iOSAutoFillClearCiphersCacheKey = "iOSAutoFillClearCiphersCache"; + public static string iOSAutoFillBiometricIntegrityKey = "iOSAutoFillBiometricIntegrityState"; public static string iOSExtensionClearCiphersCacheKey = "iOSExtensionClearCiphersCache"; + public static string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState"; public static string MigratedFromV1 = "migratedFromV1"; public static string MigratedFromV1AutofillPromptShown = "migratedV1AutofillPromptShown"; public static string TriedV1Resync = "triedV1Resync"; diff --git a/src/iOS.Autofill/LockPasswordViewController.cs b/src/iOS.Autofill/LockPasswordViewController.cs index 11b3343a3..ecadef71a 100644 --- a/src/iOS.Autofill/LockPasswordViewController.cs +++ b/src/iOS.Autofill/LockPasswordViewController.cs @@ -8,7 +8,7 @@ namespace Bit.iOS.Autofill public LockPasswordViewController(IntPtr handle) : base(handle) { - BiometricIntegrityKey = "autofillBiometricState"; + BiometricIntegrityKey = Bit.Core.Constants.iOSAutoFillBiometricIntegrityKey; DismissModalAction = Cancel; } diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs index 346d53a90..a116a9079 100644 --- a/src/iOS.Core/Controllers/LockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -59,6 +59,8 @@ namespace Bit.iOS.Core.Controllers _pinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2; _biometricLock = _vaultTimeoutService.IsBiometricLockSetAsync().GetAwaiter().GetResult() && _cryptoService.HasKeyAsync().GetAwaiter().GetResult(); + _biometricIntegrityValid = _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey).GetAwaiter() + .GetResult(); BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword; BaseCancelButton.Title = AppResources.Cancel; @@ -88,8 +90,6 @@ namespace Bit.iOS.Core.Controllers if (_biometricLock) { - _biometricIntegrityValid = _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey).GetAwaiter() - .GetResult(); if (!_biometricIntegrityValid) { return; @@ -293,7 +293,7 @@ namespace Bit.iOS.Core.Controllers cell.TextLabel.Font = ThemeHelpers.GetDangerFont(); cell.TextLabel.Lines = 0; cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap; - cell.TextLabel.Text = AppResources.BiometricInvalidated; + cell.TextLabel.Text = AppResources.BiometricInvalidatedExtension; } return cell; } diff --git a/src/iOS.Core/Services/BiometricService.cs b/src/iOS.Core/Services/BiometricService.cs index 8df66c4a1..b3fb0f65e 100644 --- a/src/iOS.Core/Services/BiometricService.cs +++ b/src/iOS.Core/Services/BiometricService.cs @@ -18,7 +18,7 @@ namespace Bit.iOS.Core.Services { if (bioIntegrityKey == null) { - bioIntegrityKey = "biometricState"; + bioIntegrityKey = Bit.Core.Constants.BiometricIntegrityKey; } var state = GetState(); if (state != null) @@ -31,28 +31,27 @@ namespace Bit.iOS.Core.Services public async Task ValidateIntegrityAsync(string bioIntegrityKey = null) { + var state = GetState(); + if (state == null) + { + // Fallback for devices unable to retrieve state + return true; + } + if (bioIntegrityKey == null) { - bioIntegrityKey = "biometricState"; + bioIntegrityKey = Bit.Core.Constants.BiometricIntegrityKey; } var oldState = await _storageService.GetAsync(bioIntegrityKey); if (oldState == null) { - // Fallback for upgraded devices - await SetupBiometricAsync(bioIntegrityKey); - - return true; + oldState = await GetMigratedIntegrityState(bioIntegrityKey); } - else + if (oldState != null) { - var state = GetState(); - if (state != null) - { - return FromBase64(oldState).Equals(state); - } - - return true; + return FromBase64(oldState).Equals(state); } + return false; } private NSData GetState() @@ -73,5 +72,35 @@ namespace Bit.iOS.Core.Services var bytes = System.Convert.FromBase64String(data); return NSData.FromArray(bytes); } + + private async Task 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(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; + } } } diff --git a/src/iOS.Extension/LockPasswordViewController.cs b/src/iOS.Extension/LockPasswordViewController.cs index 34995b614..8610f8509 100644 --- a/src/iOS.Extension/LockPasswordViewController.cs +++ b/src/iOS.Extension/LockPasswordViewController.cs @@ -9,7 +9,7 @@ namespace Bit.iOS.Extension public LockPasswordViewController(IntPtr handle) : base(handle) { - BiometricIntegrityKey = "extensionBiometricState"; + BiometricIntegrityKey = Bit.Core.Constants.iOSExtensionBiometricIntegrityKey; DismissModalAction = Cancel; }