PM-2575 Fixed extension freeze when using the return button on the keyboard when unlocking the extension. Also added way to prevent multiple executions of checking the password and logging exceptions. (#2568)
This commit is contained in:
parent
1332ef7b43
commit
98705e443f
|
@ -6435,7 +6435,7 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.
|
/// Looks up a localized string similar to Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve {
|
public static string UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve {
|
||||||
get {
|
get {
|
||||||
|
|
|
@ -2635,6 +2635,6 @@ Do you want to switch to this account?</value>
|
||||||
<value>Master password re-prompt help</value>
|
<value>Master password re-prompt help</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
|
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
|
||||||
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve</value>
|
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Bit.iOS.Core.Utilities;
|
using Bit.iOS.Core.Utilities;
|
||||||
using UIKit;
|
using UIKit;
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ namespace Bit.iOS.Autofill
|
||||||
|
|
||||||
partial void SubmitButton_Activated(UIBarButtonItem sender)
|
partial void SubmitButton_Activated(UIBarButtonItem sender)
|
||||||
{
|
{
|
||||||
var task = CheckPasswordAsync();
|
CheckPasswordAsync().FireAndForget();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void CancelButton_Activated(UIBarButtonItem sender)
|
partial void CancelButton_Activated(UIBarButtonItem sender)
|
||||||
|
|
|
@ -36,6 +36,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
private bool _passwordReprompt = false;
|
private bool _passwordReprompt = false;
|
||||||
private bool _usesKeyConnector;
|
private bool _usesKeyConnector;
|
||||||
private bool _biometricUnlockOnly = false;
|
private bool _biometricUnlockOnly = false;
|
||||||
|
private bool _checkingPassword;
|
||||||
|
|
||||||
protected bool autofillExtension = false;
|
protected bool autofillExtension = false;
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
||||||
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
||||||
{
|
{
|
||||||
CheckPasswordAsync().GetAwaiter().GetResult();
|
CheckPasswordAsync().FireAndForget();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (_pinLock)
|
if (_pinLock)
|
||||||
|
@ -208,108 +209,121 @@ namespace Bit.iOS.Core.Controllers
|
||||||
MasterPasswordCell.TextField.BecomeFirstResponder();
|
MasterPasswordCell.TextField.BecomeFirstResponder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task CheckPasswordAsync()
|
protected async Task CheckPasswordAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(MasterPasswordCell.TextField.Text))
|
if (_checkingPassword)
|
||||||
{
|
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
|
||||||
string.Format(AppResources.ValidationFieldRequired,
|
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword),
|
|
||||||
AppResources.Ok);
|
|
||||||
PresentViewController(alert, true, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var email = await _stateService.GetEmailAsync();
|
|
||||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
|
||||||
var inputtedValue = MasterPasswordCell.TextField.Text;
|
|
||||||
|
|
||||||
// HACK: iOS extensions have constrained memory, given how it works Argon2Id, it's likely to crash
|
|
||||||
// the extension depending on the argon2id memory configured.
|
|
||||||
// So, we warn the user and advise to decrease the configured memory letting them the option to continue, if wanted.
|
|
||||||
if (kdfConfig.Type == KdfType.Argon2id
|
|
||||||
&&
|
|
||||||
kdfConfig.Memory > Constants.MaximumArgon2IdMemoryBeforeExtensionCrashing
|
|
||||||
&&
|
|
||||||
!await _platformUtilsService.ShowDialogAsync(AppResources.UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve, AppResources.Warning, AppResources.Continue, AppResources.Cancel))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_checkingPassword = true;
|
||||||
|
|
||||||
if (_pinLock)
|
try
|
||||||
{
|
{
|
||||||
var failed = true;
|
if (string.IsNullOrWhiteSpace(MasterPasswordCell.TextField.Text))
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
|
string.Format(AppResources.ValidationFieldRequired,
|
||||||
|
_pinLock ? AppResources.PIN : AppResources.MasterPassword),
|
||||||
|
AppResources.Ok);
|
||||||
|
PresentViewController(alert, true, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var email = await _stateService.GetEmailAsync();
|
||||||
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
|
var inputtedValue = MasterPasswordCell.TextField.Text;
|
||||||
|
|
||||||
|
// HACK: iOS extensions have constrained memory, given how it works Argon2Id, it's likely to crash
|
||||||
|
// the extension depending on the argon2id memory configured.
|
||||||
|
// So, we warn the user and advise to decrease the configured memory letting them the option to continue, if wanted.
|
||||||
|
if (kdfConfig.Type == KdfType.Argon2id
|
||||||
|
&&
|
||||||
|
kdfConfig.Memory > Constants.MaximumArgon2IdMemoryBeforeExtensionCrashing
|
||||||
|
&&
|
||||||
|
!await _platformUtilsService.ShowDialogAsync(AppResources.UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve, AppResources.Warning, AppResources.Continue, AppResources.Cancel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pinLock)
|
||||||
|
{
|
||||||
|
var failed = true;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
if (_isPinProtected)
|
||||||
kdfConfig,
|
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
failed = decPin != inputtedValue;
|
|
||||||
if (!failed)
|
|
||||||
{
|
{
|
||||||
|
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
||||||
|
kdfConfig,
|
||||||
|
await _stateService.GetPinProtectedKeyAsync());
|
||||||
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
|
failed = decPin != inputtedValue;
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
await SetKeyAndContinueAsync(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
||||||
|
kdfConfig);
|
||||||
|
failed = false;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(key2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
catch
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
failed = true;
|
||||||
kdfConfig);
|
|
||||||
failed = false;
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
|
||||||
await SetKeyAndContinueAsync(key2);
|
|
||||||
}
|
}
|
||||||
}
|
if (failed)
|
||||||
catch
|
|
||||||
{
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
if (failed)
|
|
||||||
{
|
|
||||||
await HandleFailedCredentialsAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig);
|
|
||||||
|
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
|
||||||
if (storedKeyHash == null)
|
|
||||||
{
|
|
||||||
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
|
||||||
if (key2.KeyB64 == oldKey)
|
|
||||||
{
|
{
|
||||||
var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization);
|
await HandleFailedCredentialsAsync();
|
||||||
await _secureStorageService.RemoveAsync("oldKey");
|
|
||||||
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, key2);
|
|
||||||
if (passwordValid)
|
|
||||||
{
|
|
||||||
if (_isPinProtected)
|
|
||||||
{
|
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key2);
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email,
|
|
||||||
kdfConfig);
|
|
||||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey));
|
|
||||||
}
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
|
||||||
await SetKeyAndContinueAsync(key2, true);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await HandleFailedCredentialsAsync();
|
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig);
|
||||||
|
|
||||||
|
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||||
|
if (storedKeyHash == null)
|
||||||
|
{
|
||||||
|
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
||||||
|
if (key2.KeyB64 == oldKey)
|
||||||
|
{
|
||||||
|
var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization);
|
||||||
|
await _secureStorageService.RemoveAsync("oldKey");
|
||||||
|
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, key2);
|
||||||
|
if (passwordValid)
|
||||||
|
{
|
||||||
|
if (_isPinProtected)
|
||||||
|
{
|
||||||
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
var encKey = await _cryptoService.GetEncKeyAsync(key2);
|
||||||
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
|
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email,
|
||||||
|
kdfConfig);
|
||||||
|
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey));
|
||||||
|
}
|
||||||
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
await SetKeyAndContinueAsync(key2, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await HandleFailedCredentialsAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_checkingPassword = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleFailedCredentialsAsync()
|
private async Task HandleFailedCredentialsAsync()
|
||||||
|
|
Loading…
Reference in New Issue