1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-11-15 10:25:20 +01:00

PM-6706 Add maximum attempts to UV with MP and with PIN (#3079)

This commit is contained in:
Federico Maccaroni 2024-03-14 18:22:38 -03:00 committed by GitHub
parent 970d3c2621
commit 39da2a82c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 63 additions and 32 deletions

View File

@ -9,6 +9,6 @@ namespace Bit.Core.Abstractions
Task<bool> ShouldPerformMasterPasswordRepromptAsync(Fido2UserVerificationOptions options);
Task<(bool CanPerfom, bool IsUnlocked)> PerformOSUnlockAsync();
Task<(bool canPerformUnlockWithPin, bool pinVerified)> VerifyPinCodeAsync();
Task<(bool canPerformUnlockWithMasterPassword, bool mpVerified)> VerifyMasterPasswordAsync();
Task<(bool canPerformUnlockWithMasterPassword, bool mpVerified)> VerifyMasterPasswordAsync(bool isMasterPasswordReprompt);
}
}

View File

@ -40,7 +40,7 @@ namespace Bit.Core.Services.UserVerification
return pinVerified;
}
var (canPerformUnlockWithMasterPassword, mpVerified) = await _userVerificationMediatorService.VerifyMasterPasswordAsync();
var (canPerformUnlockWithMasterPassword, mpVerified) = await _userVerificationMediatorService.VerifyMasterPasswordAsync(false);
if (canPerformUnlockWithMasterPassword)
{
return mpVerified;

View File

@ -10,6 +10,8 @@ namespace Bit.Core.Services.UserVerification
{
public class UserVerificationMediatorService : IUserVerificationMediatorService
{
private const byte MAX_ATTEMPTS = 5;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IPasswordRepromptService _passwordRepromptService;
private readonly IUserPinService _userPinService;
@ -44,7 +46,8 @@ namespace Bit.Core.Services.UserVerification
await options.OnNeedUITask();
}
return await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(Enums.CipherRepromptType.Password);
var (canPerformMP, mpVerified) = await VerifyMasterPasswordAsync(true);
return canPerformMP && mpVerified;
}
if (!_fido2UserVerificationStrategies.TryGetValue(options.UserVerificationPreference, out var userVerificationServiceStrategy))
@ -95,43 +98,71 @@ namespace Bit.Core.Services.UserVerification
public async Task<(bool canPerformUnlockWithPin, bool pinVerified)> VerifyPinCodeAsync()
{
if (!await _userPinService.IsPinLockEnabledAsync())
return await VerifyWithAttemptsAsync(async () =>
{
return (false, false);
}
if (!await _userPinService.IsPinLockEnabledAsync())
{
return (false, false);
}
var pin = await _deviceActionService.DisplayPromptAync(AppResources.EnterPIN,
AppResources.VerifyPIN, null, AppResources.Ok, AppResources.Cancel, password: true);
if (pin is null)
{
// cancelled by the user
return (true, false);
}
var pin = await _deviceActionService.DisplayPromptAync(AppResources.EnterPIN,
AppResources.VerifyPIN, null, AppResources.Ok, AppResources.Cancel, password: true);
if (pin is null)
{
// cancelled by the user
return (true, false);
}
try
{
var isVerified = await _userPinService.VerifyPinAsync(pin);
return (true, isVerified);
}
catch (SymmetricCryptoKey.ArgumentKeyNullException)
{
return (true, false);
}
catch (SymmetricCryptoKey.InvalidKeyOperationException)
{
return (true, false);
}
try
{
var isVerified = await _userPinService.VerifyPinAsync(pin);
return (true, isVerified);
}
catch (SymmetricCryptoKey.ArgumentKeyNullException)
{
return (true, false);
}
catch (SymmetricCryptoKey.InvalidKeyOperationException)
{
return (true, false);
}
});
}
public async Task<(bool canPerformUnlockWithMasterPassword, bool mpVerified)> VerifyMasterPasswordAsync()
public async Task<(bool canPerformUnlockWithMasterPassword, bool mpVerified)> VerifyMasterPasswordAsync(bool isMasterPasswordReprompt)
{
if (!await _userVerificationService.HasMasterPasswordAsync(true))
return await VerifyWithAttemptsAsync(async () =>
{
return (false, false);
}
if (!await _userVerificationService.HasMasterPasswordAsync(true))
{
return (false, false);
}
var (_, isValid) = await _platformUtilsService.ShowPasswordDialogAndGetItAsync(AppResources.MasterPassword, string.Empty, _userVerificationService.VerifyMasterPasswordAsync);
return (true, isValid);
var title = isMasterPasswordReprompt ? AppResources.PasswordConfirmation : AppResources.MasterPassword;
var body = isMasterPasswordReprompt ? AppResources.PasswordConfirmationDesc : string.Empty;
var (_, isValid) = await _platformUtilsService.ShowPasswordDialogAndGetItAsync(title, body, _userVerificationService.VerifyMasterPasswordAsync);
return (true, isValid);
});
}
private async Task<(bool canPerform, bool isVerified)> VerifyWithAttemptsAsync(Func<Task<(bool canPerform, bool isVerified)>> verifyAsync)
{
byte attempts = 0;
do
{
var (canPerform, verified) = await verifyAsync();
if (!canPerform)
{
return (false, false);
}
if (verified)
{
return (true, true);
}
} while (attempts++ < MAX_ATTEMPTS);
return (true, false);
}
}
}