track failed unlock attempts in storage (#1421)

This commit is contained in:
Matt Portune 2021-06-09 10:03:05 -04:00 committed by GitHub
parent 80a33e98a2
commit 33791a03ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 7 deletions

View file

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

View file

@ -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)
{ {

View file

@ -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);

View file

@ -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>

View file

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

View file

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

View file

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