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
This commit is contained in:
Matt Portune 2020-12-01 15:30:23 -05:00 committed by GitHub
parent e27370cf32
commit ffd8f9951f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 20 deletions

View file

@ -2943,6 +2943,12 @@ namespace Bit.App.Resources {
} }
} }
public static string BiometricInvalidatedExtension {
get {
return ResourceManager.GetString("BiometricInvalidatedExtension", resourceCulture);
}
}
public static string EnableSyncOnRefresh { public static string EnableSyncOnRefresh {
get { get {
return ResourceManager.GetString("EnableSyncOnRefresh", resourceCulture); return ResourceManager.GetString("EnableSyncOnRefresh", resourceCulture);

View file

@ -1675,7 +1675,10 @@
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="BiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Biometric change detected, login using Master Password to enable again.</value> <value>Biometric unlock disabled pending verification of master password.</value>
</data>
<data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Biometric unlock for autofill disabled pending verification of master password.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Enable sync on refresh</value> <value>Enable sync on refresh</value>

View file

@ -32,8 +32,11 @@ namespace Bit.App.Services
Constants.MigratedFromV1AutofillPromptShown, Constants.MigratedFromV1AutofillPromptShown,
Constants.TriedV1Resync, Constants.TriedV1Resync,
Constants.ClearCiphersCacheKey, Constants.ClearCiphersCacheKey,
Constants.BiometricIntegrityKey,
Constants.iOSAutoFillClearCiphersCacheKey, Constants.iOSAutoFillClearCiphersCacheKey,
Constants.iOSAutoFillBiometricIntegrityKey,
Constants.iOSExtensionClearCiphersCacheKey, Constants.iOSExtensionClearCiphersCacheKey,
Constants.iOSExtensionBiometricIntegrityKey,
Constants.EnvironmentUrlsKey, Constants.EnvironmentUrlsKey,
Constants.InlineAutofillEnabledKey, Constants.InlineAutofillEnabledKey,
}; };

View file

@ -29,8 +29,11 @@
public static string OldUserIdKey = "userId"; public static string OldUserIdKey = "userId";
public static string AddSitePromptShownKey = "addSitePromptShown"; public static string AddSitePromptShownKey = "addSitePromptShown";
public static string ClearCiphersCacheKey = "clearCiphersCache"; public static string ClearCiphersCacheKey = "clearCiphersCache";
public static string BiometricIntegrityKey = "biometricIntegrityState";
public static string iOSAutoFillClearCiphersCacheKey = "iOSAutoFillClearCiphersCache"; public static string iOSAutoFillClearCiphersCacheKey = "iOSAutoFillClearCiphersCache";
public static string iOSAutoFillBiometricIntegrityKey = "iOSAutoFillBiometricIntegrityState";
public static string iOSExtensionClearCiphersCacheKey = "iOSExtensionClearCiphersCache"; public static string iOSExtensionClearCiphersCacheKey = "iOSExtensionClearCiphersCache";
public static string iOSExtensionBiometricIntegrityKey = "iOSExtensionBiometricIntegrityState";
public static string MigratedFromV1 = "migratedFromV1"; public static string MigratedFromV1 = "migratedFromV1";
public static string MigratedFromV1AutofillPromptShown = "migratedV1AutofillPromptShown"; public static string MigratedFromV1AutofillPromptShown = "migratedV1AutofillPromptShown";
public static string TriedV1Resync = "triedV1Resync"; public static string TriedV1Resync = "triedV1Resync";

View file

@ -8,7 +8,7 @@ namespace Bit.iOS.Autofill
public LockPasswordViewController(IntPtr handle) public LockPasswordViewController(IntPtr handle)
: base(handle) : base(handle)
{ {
BiometricIntegrityKey = "autofillBiometricState"; BiometricIntegrityKey = Bit.Core.Constants.iOSAutoFillBiometricIntegrityKey;
DismissModalAction = Cancel; DismissModalAction = Cancel;
} }

View file

@ -59,6 +59,8 @@ namespace Bit.iOS.Core.Controllers
_pinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2; _pinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
_biometricLock = _vaultTimeoutService.IsBiometricLockSetAsync().GetAwaiter().GetResult() && _biometricLock = _vaultTimeoutService.IsBiometricLockSetAsync().GetAwaiter().GetResult() &&
_cryptoService.HasKeyAsync().GetAwaiter().GetResult(); _cryptoService.HasKeyAsync().GetAwaiter().GetResult();
_biometricIntegrityValid = _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey).GetAwaiter()
.GetResult();
BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword; BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword;
BaseCancelButton.Title = AppResources.Cancel; BaseCancelButton.Title = AppResources.Cancel;
@ -88,8 +90,6 @@ namespace Bit.iOS.Core.Controllers
if (_biometricLock) if (_biometricLock)
{ {
_biometricIntegrityValid = _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey).GetAwaiter()
.GetResult();
if (!_biometricIntegrityValid) if (!_biometricIntegrityValid)
{ {
return; return;
@ -293,7 +293,7 @@ namespace Bit.iOS.Core.Controllers
cell.TextLabel.Font = ThemeHelpers.GetDangerFont(); cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
cell.TextLabel.Lines = 0; cell.TextLabel.Lines = 0;
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap; cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
cell.TextLabel.Text = AppResources.BiometricInvalidated; cell.TextLabel.Text = AppResources.BiometricInvalidatedExtension;
} }
return cell; return cell;
} }

View file

@ -18,7 +18,7 @@ namespace Bit.iOS.Core.Services
{ {
if (bioIntegrityKey == null) if (bioIntegrityKey == null)
{ {
bioIntegrityKey = "biometricState"; bioIntegrityKey = Bit.Core.Constants.BiometricIntegrityKey;
} }
var state = GetState(); var state = GetState();
if (state != null) if (state != null)
@ -31,28 +31,27 @@ namespace Bit.iOS.Core.Services
public async Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null) public async Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null)
{ {
var state = GetState();
if (state == null)
{
// Fallback for devices unable to retrieve state
return true;
}
if (bioIntegrityKey == null) if (bioIntegrityKey == null)
{ {
bioIntegrityKey = "biometricState"; bioIntegrityKey = Bit.Core.Constants.BiometricIntegrityKey;
} }
var oldState = await _storageService.GetAsync<string>(bioIntegrityKey); var oldState = await _storageService.GetAsync<string>(bioIntegrityKey);
if (oldState == null) if (oldState == null)
{ {
// Fallback for upgraded devices oldState = await GetMigratedIntegrityState(bioIntegrityKey);
await SetupBiometricAsync(bioIntegrityKey);
return true;
} }
else if (oldState != null)
{ {
var state = GetState(); return FromBase64(oldState).Equals(state);
if (state != null)
{
return FromBase64(oldState).Equals(state);
}
return true;
} }
return false;
} }
private NSData GetState() private NSData GetState()
@ -73,5 +72,35 @@ namespace Bit.iOS.Core.Services
var bytes = System.Convert.FromBase64String(data); var bytes = System.Convert.FromBase64String(data);
return NSData.FromArray(bytes); 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

@ -9,7 +9,7 @@ namespace Bit.iOS.Extension
public LockPasswordViewController(IntPtr handle) public LockPasswordViewController(IntPtr handle)
: base(handle) : base(handle)
{ {
BiometricIntegrityKey = "extensionBiometricState"; BiometricIntegrityKey = Bit.Core.Constants.iOSExtensionBiometricIntegrityKey;
DismissModalAction = Cancel; DismissModalAction = Cancel;
} }