mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 23:25:45 +03:00
track failed unlock attempts in storage (#1421)
This commit is contained in:
parent
80a33e98a2
commit
33791a03ac
7 changed files with 82 additions and 7 deletions
|
@ -8,6 +8,7 @@ using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
@ -37,7 +38,6 @@ namespace Bit.App.Pages
|
||||||
private string _biometricButtonText;
|
private string _biometricButtonText;
|
||||||
private string _loggedInAsText;
|
private string _loggedInAsText;
|
||||||
private string _lockedVerifyText;
|
private string _lockedVerifyText;
|
||||||
private int _invalidPinAttempts = 0;
|
|
||||||
private Tuple<bool, bool> _pinSet;
|
private Tuple<bool, bool> _pinSet;
|
||||||
|
|
||||||
public LockPageViewModel()
|
public LockPageViewModel()
|
||||||
|
@ -208,6 +208,7 @@ namespace Bit.App.Pages
|
||||||
if (!failed)
|
if (!failed)
|
||||||
{
|
{
|
||||||
Pin = string.Empty;
|
Pin = string.Empty;
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,6 +218,7 @@ namespace Bit.App.Pages
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||||
failed = false;
|
failed = false;
|
||||||
Pin = string.Empty;
|
Pin = string.Empty;
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,8 +228,8 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
if (failed)
|
if (failed)
|
||||||
{
|
{
|
||||||
_invalidPinAttempts++;
|
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
||||||
if (_invalidPinAttempts >= 5)
|
if (invalidUnlockAttempts >= 5)
|
||||||
{
|
{
|
||||||
_messagingService.Send("logout");
|
_messagingService.Send("logout");
|
||||||
return;
|
return;
|
||||||
|
@ -278,6 +280,7 @@ namespace Bit.App.Pages
|
||||||
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
||||||
}
|
}
|
||||||
MasterPassword = string.Empty;
|
MasterPassword = string.Empty;
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key);
|
||||||
|
|
||||||
// Re-enable biometrics
|
// Re-enable biometrics
|
||||||
|
@ -288,6 +291,12 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
||||||
|
if (invalidUnlockAttempts >= 5)
|
||||||
|
{
|
||||||
|
_messagingService.Send("logout");
|
||||||
|
return;
|
||||||
|
}
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidMasterPassword,
|
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidMasterPassword,
|
||||||
AppResources.AnErrorHasOccurred);
|
AppResources.AnErrorHasOccurred);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
@ -125,6 +126,7 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
||||||
}
|
}
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (response.TwoFactor)
|
if (response.TwoFactor)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@ using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
|
@ -182,6 +183,7 @@ namespace Bit.App.Pages
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri);
|
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri);
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
if (RememberOrgIdentifier)
|
if (RememberOrgIdentifier)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
||||||
|
|
|
@ -39,6 +39,7 @@ namespace Bit.App.Services
|
||||||
Constants.iOSExtensionBiometricIntegrityKey,
|
Constants.iOSExtensionBiometricIntegrityKey,
|
||||||
Constants.EnvironmentUrlsKey,
|
Constants.EnvironmentUrlsKey,
|
||||||
Constants.InlineAutofillEnabledKey,
|
Constants.InlineAutofillEnabledKey,
|
||||||
|
Constants.InvalidUnlockAttempts,
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly HashSet<string> _migrateToPreferences = new HashSet<string>
|
private readonly HashSet<string> _migrateToPreferences = new HashSet<string>
|
||||||
|
|
|
@ -440,5 +440,20 @@ namespace Bit.App.Utilities
|
||||||
}
|
}
|
||||||
return previousPage;
|
return previousPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<int> IncrementInvalidUnlockAttemptsAsync()
|
||||||
|
{
|
||||||
|
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
var invalidUnlockAttempts = await storageService.GetAsync<int>(Constants.InvalidUnlockAttempts);
|
||||||
|
invalidUnlockAttempts++;
|
||||||
|
await storageService.SaveAsync(Constants.InvalidUnlockAttempts, invalidUnlockAttempts);
|
||||||
|
return invalidUnlockAttempts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ResetInvalidUnlockAttemptsAsync()
|
||||||
|
{
|
||||||
|
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
await storageService.RemoveAsync(Constants.InvalidUnlockAttempts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
public static string EventCollectionKey = "eventCollection";
|
public static string EventCollectionKey = "eventCollection";
|
||||||
public static string PreviousPageKey = "previousPage";
|
public static string PreviousPageKey = "previousPage";
|
||||||
public static string InlineAutofillEnabledKey = "inlineAutofillEnabled";
|
public static string InlineAutofillEnabledKey = "inlineAutofillEnabled";
|
||||||
|
public static string InvalidUnlockAttempts = "invalidUnlockAttempts";
|
||||||
public const int SelectFileRequestCode = 42;
|
public const int SelectFileRequestCode = 42;
|
||||||
public const int SelectFilePermissionRequestCode = 43;
|
public const int SelectFilePermissionRequestCode = 43;
|
||||||
public const int SaveFileRequestCode = 44;
|
public const int SaveFileRequestCode = 44;
|
||||||
|
|
|
@ -8,6 +8,7 @@ using Bit.App.Abstractions;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
@ -27,7 +28,6 @@ namespace Bit.iOS.Core.Controllers
|
||||||
private bool _pinLock;
|
private bool _pinLock;
|
||||||
private bool _biometricLock;
|
private bool _biometricLock;
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
private int _invalidPinAttempts;
|
|
||||||
|
|
||||||
public LockPasswordViewController(IntPtr handle)
|
public LockPasswordViewController(IntPtr handle)
|
||||||
: base(handle)
|
: base(handle)
|
||||||
|
@ -144,6 +144,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
failed = decPin != inputtedValue;
|
failed = decPin != inputtedValue;
|
||||||
if (!failed)
|
if (!failed)
|
||||||
{
|
{
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +153,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||||
failed = false;
|
failed = false;
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(key2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,10 +163,10 @@ namespace Bit.iOS.Core.Controllers
|
||||||
}
|
}
|
||||||
if (failed)
|
if (failed)
|
||||||
{
|
{
|
||||||
_invalidPinAttempts++;
|
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
||||||
if (_invalidPinAttempts >= 5)
|
if (invalidUnlockAttempts >= 5)
|
||||||
{
|
{
|
||||||
Cancel?.Invoke();
|
await LogOutAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
InvalidValue();
|
InvalidValue();
|
||||||
|
@ -196,6 +198,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||||
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key2.Key, pinKey);
|
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key2.Key, pinKey);
|
||||||
}
|
}
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(key2);
|
||||||
|
|
||||||
// Re-enable biometrics
|
// Re-enable biometrics
|
||||||
|
@ -206,6 +209,12 @@ namespace Bit.iOS.Core.Controllers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
||||||
|
if (invalidUnlockAttempts >= 5)
|
||||||
|
{
|
||||||
|
await LogOutAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
InvalidValue();
|
InvalidValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,6 +266,42 @@ namespace Bit.iOS.Core.Controllers
|
||||||
PresentViewController(alert, true, null);
|
PresentViewController(alert, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LogOutAsync()
|
||||||
|
{
|
||||||
|
var syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
|
var tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
|
||||||
|
var settingsService = ServiceContainer.Resolve<ISettingsService>("settingsService");
|
||||||
|
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
|
var folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||||
|
var collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||||
|
var passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
||||||
|
"passwordGenerationService");
|
||||||
|
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
var searchService = ServiceContainer.Resolve<ISearchService>("searchService");
|
||||||
|
var authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
|
|
||||||
|
var userId = await _userService.GetUserIdAsync();
|
||||||
|
await Task.WhenAll(
|
||||||
|
syncService.SetLastSyncAsync(DateTime.MinValue),
|
||||||
|
tokenService.ClearTokenAsync(),
|
||||||
|
_cryptoService.ClearKeysAsync(),
|
||||||
|
_userService.ClearAsync(),
|
||||||
|
settingsService.ClearAsync(userId),
|
||||||
|
cipherService.ClearAsync(userId),
|
||||||
|
folderService.ClearAsync(userId),
|
||||||
|
collectionService.ClearAsync(userId),
|
||||||
|
passwordGenerationService.ClearAsync(),
|
||||||
|
_vaultTimeoutService.ClearAsync(),
|
||||||
|
stateService.PurgeAsync(),
|
||||||
|
_deviceActionService.ClearCacheAsync());
|
||||||
|
_vaultTimeoutService.BiometricLocked = true;
|
||||||
|
searchService.ClearIndex();
|
||||||
|
authService.LogOut(() =>
|
||||||
|
{
|
||||||
|
Cancel?.Invoke();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public class TableSource : ExtendedUITableViewSource
|
public class TableSource : ExtendedUITableViewSource
|
||||||
{
|
{
|
||||||
private LockPasswordViewController _controller;
|
private LockPasswordViewController _controller;
|
||||||
|
|
Loading…
Reference in a new issue