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;
}