mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
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:
parent
e27370cf32
commit
ffd8f9951f
8 changed files with 64 additions and 20 deletions
6
src/App/Resources/AppResources.Designer.cs
generated
6
src/App/Resources/AppResources.Designer.cs
generated
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue