mirror of
https://github.com/bitwarden/mobile
synced 2025-12-15 07:43:37 +00:00
[PM-2713] add migration for pin on lock screens
This commit is contained in:
@@ -46,7 +46,7 @@
|
|||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
<Grid
|
<Grid
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
IsVisible="{Binding PinLock}"
|
IsVisible="{Binding PinEnabled}"
|
||||||
Padding="0, 10, 0, 0">
|
Padding="0, 10, 0, 0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="_passwordGrid"
|
x:Name="_passwordGrid"
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding PinEnabled, Converter={StaticResource inverseBool}}"
|
||||||
Padding="0, 10, 0, 0">
|
Padding="0, 10, 0, 0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_vm?.PinLock ?? false)
|
if (_vm?.PinEnabled ?? false)
|
||||||
{
|
{
|
||||||
return _pin;
|
return _pin;
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task PromptBiometricAfterResumeAsync()
|
public async Task PromptBiometricAfterResumeAsync()
|
||||||
{
|
{
|
||||||
if (_vm.BiometricLock)
|
if (_vm.BiometricEnabled)
|
||||||
{
|
{
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
if (!_promptedAfterResume)
|
if (!_promptedAfterResume)
|
||||||
@@ -91,13 +91,13 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
_vm.FocusSecretEntry += PerformFocusSecretEntry;
|
_vm.FocusSecretEntry += PerformFocusSecretEntry;
|
||||||
|
|
||||||
if (!_vm.BiometricLock)
|
if (!_vm.BiometricEnabled)
|
||||||
{
|
{
|
||||||
RequestFocus(SecretEntry);
|
RequestFocus(SecretEntry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_vm.UsingKeyConnector && !_vm.PinLock)
|
if (_vm.UsingKeyConnector && !_vm.PinEnabled)
|
||||||
{
|
{
|
||||||
_passwordGrid.IsVisible = false;
|
_passwordGrid.IsVisible = false;
|
||||||
_unlockButton.IsVisible = false;
|
_unlockButton.IsVisible = false;
|
||||||
|
|||||||
@@ -38,16 +38,15 @@ namespace Bit.App.Pages
|
|||||||
private string _masterPassword;
|
private string _masterPassword;
|
||||||
private string _pin;
|
private string _pin;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private bool _pinLock;
|
private PinLockEnum _pinStatus;
|
||||||
private bool _biometricLock;
|
private bool _pinEnabled;
|
||||||
|
private bool _biometricEnabled;
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
private bool _biometricButtonVisible;
|
private bool _biometricButtonVisible;
|
||||||
private bool _usingKeyConnector;
|
private bool _usingKeyConnector;
|
||||||
private string _biometricButtonText;
|
private string _biometricButtonText;
|
||||||
private string _loggedInAsText;
|
private string _loggedInAsText;
|
||||||
private string _lockedVerifyText;
|
private string _lockedVerifyText;
|
||||||
private bool _isPinProtected;
|
|
||||||
private bool _isPinProtectedWithKey;
|
|
||||||
|
|
||||||
public LockPageViewModel()
|
public LockPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -100,10 +99,10 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool PinLock
|
public bool PinEnabled
|
||||||
{
|
{
|
||||||
get => _pinLock;
|
get => _pinEnabled;
|
||||||
set => SetProperty(ref _pinLock, value);
|
set => SetProperty(ref _pinEnabled, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UsingKeyConnector
|
public bool UsingKeyConnector
|
||||||
@@ -111,10 +110,10 @@ namespace Bit.App.Pages
|
|||||||
get => _usingKeyConnector;
|
get => _usingKeyConnector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool BiometricLock
|
public bool BiometricEnabled
|
||||||
{
|
{
|
||||||
get => _biometricLock;
|
get => _biometricEnabled;
|
||||||
set => SetProperty(ref _biometricLock, value);
|
set => SetProperty(ref _biometricEnabled, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool BiometricIntegrityValid
|
public bool BiometricIntegrityValid
|
||||||
@@ -162,14 +161,18 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
|
_pinStatus = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
PinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
|
|
||||||
_isPinProtectedWithKey;
|
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync()
|
||||||
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
?? await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
PinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) ||
|
||||||
|
_pinStatus == PinLockEnum.Persistent;
|
||||||
|
|
||||||
|
BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasEncryptedUserKeyAsync();
|
||||||
|
|
||||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
||||||
if (_usingKeyConnector && !(BiometricLock || PinLock))
|
if (_usingKeyConnector && !(BiometricEnabled || PinEnabled))
|
||||||
{
|
{
|
||||||
await _vaultTimeoutService.LogOutAsync();
|
await _vaultTimeoutService.LogOutAsync();
|
||||||
return;
|
return;
|
||||||
@@ -188,7 +191,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
var webVaultHostname = CoreHelpers.GetHostname(webVault);
|
var webVaultHostname = CoreHelpers.GetHostname(webVault);
|
||||||
LoggedInAsText = string.Format(AppResources.LoggedInAsOn, _email, webVaultHostname);
|
LoggedInAsText = string.Format(AppResources.LoggedInAsOn, _email, webVaultHostname);
|
||||||
if (PinLock)
|
if (PinEnabled)
|
||||||
{
|
{
|
||||||
PageTitle = AppResources.VerifyPIN;
|
PageTitle = AppResources.VerifyPIN;
|
||||||
LockedVerifyText = AppResources.VaultLockedPIN;
|
LockedVerifyText = AppResources.VaultLockedPIN;
|
||||||
@@ -207,7 +210,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BiometricLock)
|
if (BiometricEnabled)
|
||||||
{
|
{
|
||||||
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
||||||
if (!_biometricIntegrityValid)
|
if (!_biometricIntegrityValid)
|
||||||
@@ -229,14 +232,14 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
{
|
{
|
||||||
if (PinLock && string.IsNullOrWhiteSpace(Pin))
|
if (PinEnabled && string.IsNullOrWhiteSpace(Pin))
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.PIN),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.PIN),
|
||||||
AppResources.Ok);
|
AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!PinLock && string.IsNullOrWhiteSpace(MasterPassword))
|
if (!PinEnabled && string.IsNullOrWhiteSpace(MasterPassword))
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
||||||
@@ -247,34 +250,54 @@ namespace Bit.App.Pages
|
|||||||
ShowPassword = false;
|
ShowPassword = false;
|
||||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
|
|
||||||
if (PinLock)
|
if (PinEnabled)
|
||||||
{
|
{
|
||||||
var failed = true;
|
var failed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
EncString userKeyPin = null;
|
||||||
|
EncString oldPinProtected = null;
|
||||||
|
if (_pinStatus == PinLockEnum.Persistent)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
userKeyPin = await _stateService.GetUserKeyPinAsync();
|
||||||
|
var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
|
||||||
|
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
|
||||||
|
}
|
||||||
|
else if (_pinStatus == PinLockEnum.Transient)
|
||||||
|
{
|
||||||
|
userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync();
|
||||||
|
oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserKey userKey;
|
||||||
|
if (oldPinProtected != null)
|
||||||
|
{
|
||||||
|
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
|
||||||
|
_pinStatus == PinLockEnum.Transient,
|
||||||
|
Pin,
|
||||||
|
_email,
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
oldPinProtected
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
);
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
failed = decPin != Pin;
|
|
||||||
if (!failed)
|
|
||||||
{
|
|
||||||
Pin = string.Empty;
|
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
|
||||||
await SetKeyAndContinueAsync(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, kdfConfig);
|
userKey = await _cryptoService.DecryptUserKeyWithPinAsync(
|
||||||
failed = false;
|
Pin,
|
||||||
|
_email,
|
||||||
|
kdfConfig,
|
||||||
|
userKeyPin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
var decryptedPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), userKey);
|
||||||
|
failed = decryptedPin != Pin;
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
Pin = string.Empty;
|
Pin = string.Empty;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key);
|
await SetKeyAndContinueAsync(userKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -302,10 +325,12 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if (storedKeyHash != null)
|
if (storedKeyHash != null)
|
||||||
{
|
{
|
||||||
|
// Offline unlock possible
|
||||||
passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(MasterPassword, masterKey);
|
passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(MasterPassword, masterKey);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Online unlock required
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey, HashPurpose.ServerAuthorization);
|
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey, HashPurpose.ServerAuthorization);
|
||||||
var request = new PasswordVerificationRequest();
|
var request = new PasswordVerificationRequest();
|
||||||
@@ -327,16 +352,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
if (passwordValid)
|
if (passwordValid)
|
||||||
{
|
{
|
||||||
// TODO(Jake): Update this to use new PinKeyEphemeral
|
|
||||||
if (_isPinProtected)
|
|
||||||
{
|
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(masterKey);
|
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
|
||||||
var pinKey = await _cryptoService.MakePinKeyAsync(decPin, _email, kdfConfig);
|
|
||||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(masterKey.Key, pinKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
|
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
|
||||||
{
|
{
|
||||||
// Save the ForcePasswordResetReason to force a password reset after unlock
|
// Save the ForcePasswordResetReason to force a password reset after unlock
|
||||||
@@ -346,10 +361,13 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
MasterPassword = string.Empty;
|
MasterPassword = string.Empty;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(masterKey);
|
|
||||||
|
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
|
await SetKeyAndContinueAsync(userKey);
|
||||||
|
|
||||||
// Re-enable biometrics
|
// Re-enable biometrics
|
||||||
if (BiometricLock & !BiometricIntegrityValid)
|
if (BiometricEnabled & !BiometricIntegrityValid)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync();
|
await _biometricService.SetupBiometricAsync();
|
||||||
}
|
}
|
||||||
@@ -426,7 +444,7 @@ namespace Bit.App.Pages
|
|||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
var secret = PinLock ? Pin : MasterPassword;
|
var secret = PinEnabled ? Pin : MasterPassword;
|
||||||
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry));
|
_secretEntryFocusWeakEventManager.RaiseEvent(string.IsNullOrEmpty(secret) ? 0 : secret.Length, nameof(FocusSecretEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,12 +452,12 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
|
||||||
BiometricButtonVisible = BiometricIntegrityValid;
|
BiometricButtonVisible = BiometricIntegrityValid;
|
||||||
if (!BiometricLock || !BiometricIntegrityValid)
|
if (!BiometricEnabled || !BiometricIntegrityValid)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||||
PinLock ? AppResources.PIN : AppResources.MasterPassword,
|
PinEnabled ? AppResources.PIN : AppResources.MasterPassword,
|
||||||
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)));
|
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)));
|
||||||
await _stateService.SetBiometricLockedAsync(!success);
|
await _stateService.SetBiometricLockedAsync(!success);
|
||||||
if (success)
|
if (success)
|
||||||
@@ -448,7 +466,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Jake): Update to store UserKey
|
|
||||||
private async Task SetKeyAndContinueAsync(UserKey key)
|
private async Task SetKeyAndContinueAsync(UserKey key)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
_pin = pinSet.Item1 || pinSet.Item2;
|
_pin = pinSet != PinLockEnum.Disabled;
|
||||||
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
|
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
|
||||||
_screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync();
|
_screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task SetUserKeyAsync(UserKey userKey, string userId = null);
|
Task SetUserKeyAsync(UserKey userKey, string userId = null);
|
||||||
Task<UserKey> GetUserKeyAsync(string userId = null);
|
Task<UserKey> GetUserKeyAsync(string userId = null);
|
||||||
Task<bool> HasUserKeyAsync(string userId = null);
|
Task<bool> HasUserKeyAsync(string userId = null);
|
||||||
|
Task<bool> HasEncryptedUserKeyAsync(string userId = null);
|
||||||
Task<UserKey> MakeUserKeyAsync();
|
Task<UserKey> MakeUserKeyAsync();
|
||||||
Task ClearUserKeyAsync(string userId = null);
|
Task ClearUserKeyAsync(string userId = null);
|
||||||
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
||||||
@@ -38,7 +39,8 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
||||||
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
||||||
Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config);
|
Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config);
|
||||||
// Task<UserKey> DecryptUserKeyWithPin(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null);
|
Task<UserKey> DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null);
|
||||||
|
Task<MasterKey> DecryptMasterKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedMasterKey = null);
|
||||||
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
||||||
// TODO(Jake): This isn't used, delete?
|
// TODO(Jake): This isn't used, delete?
|
||||||
Task ClearKeysAsync(string userId = null);
|
Task ClearKeysAsync(string userId = null);
|
||||||
@@ -52,6 +54,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<EncString> EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
Task<EncString> EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
||||||
Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null);
|
Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null);
|
||||||
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
||||||
|
Task<UserKey> DecryptAndMigrateOldPinKeyAsync(bool masterPasswordOnRestart, string pin, string email, KdfConfig kdfConfig, EncString oldPinKey);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<bool> CanAccessPremiumAsync(string userId = null);
|
Task<bool> CanAccessPremiumAsync(string userId = null);
|
||||||
Task<string> GetProtectedPinAsync(string userId = null);
|
Task<string> GetProtectedPinAsync(string userId = null);
|
||||||
Task SetPersonalPremiumAsync(bool value, string userId = null);
|
Task SetPersonalPremiumAsync(bool value, string userId = null);
|
||||||
Task<string> GetUserKeyPinAsync(string userId = null);
|
Task<EncString> GetUserKeyPinAsync(string userId = null);
|
||||||
Task SetUserKeyPinAsync(EncString value, string userId = null);
|
Task SetUserKeyPinAsync(EncString value, string userId = null);
|
||||||
Task<EncString> GetUserKeyPinEphemeralAsync(string userId = null);
|
Task<EncString> GetUserKeyPinEphemeralAsync(string userId = null);
|
||||||
Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null);
|
Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.Core.Abstractions
|
namespace Bit.Core.Abstractions
|
||||||
{
|
{
|
||||||
@@ -16,7 +17,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<bool> ShouldLockAsync(string userId = null);
|
Task<bool> ShouldLockAsync(string userId = null);
|
||||||
Task<bool> IsLoggedOutByTimeoutAsync(string userId = null);
|
Task<bool> IsLoggedOutByTimeoutAsync(string userId = null);
|
||||||
Task<bool> ShouldLogOutByTimeoutAsync(string userId = null);
|
Task<bool> ShouldLogOutByTimeoutAsync(string userId = null);
|
||||||
Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null);
|
Task<PinLockEnum> IsPinLockSetAsync(string userId = null);
|
||||||
Task<bool> IsBiometricLockSetAsync(string userId = null);
|
Task<bool> IsBiometricLockSetAsync(string userId = null);
|
||||||
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null);
|
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null);
|
||||||
Task LogOutAsync(bool userInitiated = true, string userId = null);
|
Task LogOutAsync(bool userInitiated = true, string userId = null);
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ namespace Bit.Core.Services
|
|||||||
return await GetUserKeyAsync(userId) != null;
|
return await GetUserKeyAsync(userId) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HasEncryptedUserKeyAsync(string userId = null)
|
||||||
|
{
|
||||||
|
return await _stateService.GetUserKeyMasterKeyAsync(userId) != null;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<UserKey> MakeUserKeyAsync()
|
public async Task<UserKey> MakeUserKeyAsync()
|
||||||
{
|
{
|
||||||
return new UserKey(await _cryptoFunctionService.RandomBytesAsync(64));
|
return new UserKey(await _cryptoFunctionService.RandomBytesAsync(64));
|
||||||
@@ -418,18 +423,39 @@ namespace Bit.Core.Services
|
|||||||
await clearDeprecatedPinKeysAsync(userId);
|
await clearDeprecatedPinKeysAsync(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public async Task<UserKey> DecryptUserKeyWithPin(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null)
|
public async Task<UserKey> DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null)
|
||||||
// {
|
{
|
||||||
// pinProtectedUserKey ??= await _stateService.GetUserKeyPinAsync();
|
pinProtectedUserKey ??= await _stateService.GetUserKeyPinAsync();
|
||||||
// pinProtectedUserKey ??= await _stateService.GetUserKeyPinEphemeralAsync();
|
pinProtectedUserKey ??= await _stateService.GetUserKeyPinEphemeralAsync();
|
||||||
// if (pinProtectedUserKey == null)
|
if (pinProtectedUserKey == null)
|
||||||
// {
|
{
|
||||||
// throw new Exception("No PIN protected user key found.");
|
throw new Exception("No PIN protected user key found.");
|
||||||
// }
|
}
|
||||||
// var pinKey = await MakePinKeyAsync(pin, salt, kdfConfig);
|
var pinKey = await MakePinKeyAsync(pin, salt, kdfConfig);
|
||||||
// var userKey = await DecryptToBytesAsync(pinProtectedUserKey, pinKey);
|
var userKey = await DecryptToBytesAsync(pinProtectedUserKey, pinKey);
|
||||||
// return new UserKey(userKey);
|
return new UserKey(userKey);
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
// Only for migration purposes
|
||||||
|
public async Task<MasterKey> DecryptMasterKeyWithPinAsync(
|
||||||
|
string pin,
|
||||||
|
string salt,
|
||||||
|
KdfConfig kdfConfig,
|
||||||
|
EncString pinProtectedMasterKey = null)
|
||||||
|
{
|
||||||
|
if (pinProtectedMasterKey == null)
|
||||||
|
{
|
||||||
|
var pinProtectedMasterKeyString = await _stateService.GetPinProtectedAsync();
|
||||||
|
if (pinProtectedMasterKeyString == null)
|
||||||
|
{
|
||||||
|
throw new Exception("No PIN protected master key found.");
|
||||||
|
}
|
||||||
|
pinProtectedMasterKey = new EncString(pinProtectedMasterKeyString);
|
||||||
|
}
|
||||||
|
var pinKey = await MakePinKeyAsync(pin, salt, kdfConfig);
|
||||||
|
var masterKey = await DecryptToBytesAsync(pinProtectedMasterKey, pinKey);
|
||||||
|
return new MasterKey(masterKey);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial)
|
public async Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial)
|
||||||
{
|
{
|
||||||
@@ -932,10 +958,52 @@ namespace Bit.Core.Services
|
|||||||
public SymmetricCryptoKey Key { get; set; }
|
public SymmetricCryptoKey Key { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --LEGACY METHODS--
|
// --MIGRATION METHODS--
|
||||||
// We previously used the master key for additional keys, but now we use the user key.
|
// We previously used the master key for additional keys, but now we use the user key.
|
||||||
// These methods support migrating the old keys to the new ones.
|
// These methods support migrating the old keys to the new ones.
|
||||||
|
|
||||||
|
public async Task<UserKey> DecryptAndMigrateOldPinKeyAsync(
|
||||||
|
bool masterPasswordOnRestart,
|
||||||
|
string pin,
|
||||||
|
string email,
|
||||||
|
KdfConfig kdfConfig,
|
||||||
|
EncString oldPinKey)
|
||||||
|
{
|
||||||
|
// Decrypt
|
||||||
|
var masterKey = await DecryptMasterKeyWithPinAsync(
|
||||||
|
pin,
|
||||||
|
email,
|
||||||
|
kdfConfig,
|
||||||
|
oldPinKey
|
||||||
|
);
|
||||||
|
var encUserKey = await _stateService.GetEncKeyEncryptedAsync();
|
||||||
|
var userKey = await DecryptUserKeyWithMasterKeyAsync(
|
||||||
|
masterKey,
|
||||||
|
new EncString(encUserKey)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Migrate
|
||||||
|
var pinKey = await MakePinKeyAsync(pin, email, kdfConfig);
|
||||||
|
var pinProtectedKey = await EncryptAsync(userKey.Key, pinKey);
|
||||||
|
|
||||||
|
if (masterPasswordOnRestart)
|
||||||
|
{
|
||||||
|
await _stateService.SetPinProtectedKeyAsync(null);
|
||||||
|
await _stateService.SetUserKeyPinEphemeralAsync(pinProtectedKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _stateService.SetPinProtectedAsync(null);
|
||||||
|
await _stateService.SetUserKeyPinAsync(pinProtectedKey);
|
||||||
|
|
||||||
|
// We previously only set the protected pin if MP on Restart was enabled
|
||||||
|
// now we set it regardless
|
||||||
|
var encPin = await EncryptAsync(pin, userKey);
|
||||||
|
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||||
|
}
|
||||||
|
return userKey;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task clearDeprecatedPinKeysAsync(string userId = null)
|
public async Task clearDeprecatedPinKeysAsync(string userId = null)
|
||||||
{
|
{
|
||||||
await _stateService.SetPinProtectedAsync(null);
|
await _stateService.SetPinProtectedAsync(null);
|
||||||
|
|||||||
@@ -396,9 +396,9 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Jake): Does this need to be secure storage?
|
// TODO(Jake): Does this need to be secure storage?
|
||||||
public async Task<string> GetUserKeyPinAsync(string userId = null)
|
public async Task<EncString> GetUserKeyPinAsync(string userId = null)
|
||||||
{
|
{
|
||||||
return await _storageMediatorService.GetAsync<string>(Constants.UserKeyPinKey(userId), false);
|
return new EncString(await _storageMediatorService.GetAsync<string>(Constants.UserKeyPinKey(userId), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Jake): Does this need to be secure storage?
|
// TODO(Jake): Does this need to be secure storage?
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ using Bit.Core.Models.Domain;
|
|||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
|
public enum PinLockEnum
|
||||||
|
{
|
||||||
|
Disabled,
|
||||||
|
Persistent,
|
||||||
|
Transient
|
||||||
|
}
|
||||||
|
|
||||||
public class VaultTimeoutService : IVaultTimeoutService
|
public class VaultTimeoutService : IVaultTimeoutService
|
||||||
{
|
{
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
@@ -165,11 +172,13 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
if (await _keyConnectorService.GetUsesKeyConnector())
|
if (await _keyConnectorService.GetUsesKeyConnector())
|
||||||
{
|
{
|
||||||
var (isPinProtected, isPinProtectedWithKey) = await IsPinLockSetAsync(userId);
|
var pinStatus = await IsPinLockSetAsync(userId);
|
||||||
var pinLock = (isPinProtected && await _stateService.GetPinProtectedKeyAsync(userId) != null) ||
|
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync()
|
||||||
isPinProtectedWithKey;
|
?? await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
var pinEnabled = (pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) ||
|
||||||
|
pinStatus == PinLockEnum.Persistent;
|
||||||
|
|
||||||
if (!pinLock && !await IsBiometricLockSetAsync())
|
if (!pinEnabled && !await IsBiometricLockSetAsync())
|
||||||
{
|
{
|
||||||
await LogOutAsync(userInitiated, userId);
|
await LogOutAsync(userInitiated, userId);
|
||||||
return;
|
return;
|
||||||
@@ -218,11 +227,26 @@ namespace Bit.Core.Services
|
|||||||
await _tokenService.ToggleTokensAsync();
|
await _tokenService.ToggleTokensAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tuple<bool, bool>> IsPinLockSetAsync(string userId = null)
|
public async Task<PinLockEnum> IsPinLockSetAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var protectedPin = await _stateService.GetProtectedPinAsync(userId);
|
// we can't depend on only the protected pin being set because old
|
||||||
var pinProtectedKey = await _stateService.GetPinProtectedAsync(userId);
|
// versions only used it for MP on Restart
|
||||||
return new Tuple<bool, bool>(protectedPin != null, pinProtectedKey != null);
|
var pinIsEnabled = await _stateService.GetProtectedPinAsync(userId);
|
||||||
|
var userKeyPin = await _stateService.GetUserKeyPinAsync(userId);
|
||||||
|
var oldUserKeyPin = await _stateService.GetPinProtectedAsync(userId);
|
||||||
|
|
||||||
|
if (userKeyPin != null || oldUserKeyPin != null)
|
||||||
|
{
|
||||||
|
return PinLockEnum.Persistent;
|
||||||
|
}
|
||||||
|
else if (pinIsEnabled != null && userKeyPin == null && oldUserKeyPin == null)
|
||||||
|
{
|
||||||
|
return PinLockEnum.Transient;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return PinLockEnum.Disabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsBiometricLockSetAsync(string userId = null)
|
public async Task<bool> IsBiometricLockSetAsync(string userId = null)
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ using Bit.App.Utilities;
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.iOS.Core.Utilities;
|
using Bit.iOS.Core.Utilities;
|
||||||
using Bit.iOS.Core.Views;
|
using Bit.iOS.Core.Views;
|
||||||
using Foundation;
|
using Foundation;
|
||||||
using UIKit;
|
using UIKit;
|
||||||
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.iOS.Core.Controllers
|
namespace Bit.iOS.Core.Controllers
|
||||||
@@ -28,10 +30,9 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private IBiometricService _biometricService;
|
private IBiometricService _biometricService;
|
||||||
private IKeyConnectorService _keyConnectorService;
|
private IKeyConnectorService _keyConnectorService;
|
||||||
private IAccountsManager _accountManager;
|
private IAccountsManager _accountManager;
|
||||||
private bool _isPinProtected;
|
private PinLockEnum _pinStatus;
|
||||||
private bool _isPinProtectedWithKey;
|
private bool _pinEnabled;
|
||||||
private bool _pinLock;
|
private bool _biometricEnabled;
|
||||||
private bool _biometricLock;
|
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
private bool _passwordReprompt = false;
|
private bool _passwordReprompt = false;
|
||||||
private bool _usesKeyConnector;
|
private bool _usesKeyConnector;
|
||||||
@@ -85,7 +86,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract UITableView TableView { get; }
|
public abstract UITableView TableView { get; }
|
||||||
|
|
||||||
public override async void ViewDidLoad()
|
public override async void ViewDidLoad()
|
||||||
{
|
{
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
@@ -103,25 +104,28 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
|
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
|
||||||
{
|
{
|
||||||
_passwordReprompt = true;
|
_passwordReprompt = true;
|
||||||
_isPinProtected = false;
|
_pinStatus = PinLockEnum.Disabled;
|
||||||
_isPinProtectedWithKey = false;
|
_pinEnabled = false;
|
||||||
_pinLock = false;
|
_biometricEnabled = false;
|
||||||
_biometricLock = false;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
|
_pinStatus = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
_pinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
|
|
||||||
_isPinProtectedWithKey;
|
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync()
|
||||||
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
|
?? await _stateService.GetPinProtectedKeyAsync();
|
||||||
await _cryptoService.HasKeyAsync();
|
_pinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) ||
|
||||||
|
_pinStatus == PinLockEnum.Persistent;
|
||||||
|
|
||||||
|
_biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync()
|
||||||
|
&& await _cryptoService.HasEncryptedUserKeyAsync();
|
||||||
_biometricIntegrityValid =
|
_biometricIntegrityValid =
|
||||||
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
||||||
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
||||||
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
|
_biometricUnlockOnly = _usesKeyConnector && _biometricEnabled && !_pinEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
BaseNavItem.Title = AppResources.VerifyPIN;
|
BaseNavItem.Title = AppResources.VerifyPIN;
|
||||||
}
|
}
|
||||||
@@ -150,7 +154,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
if (!_biometricUnlockOnly)
|
if (!_biometricUnlockOnly)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.Label.Text = _pinLock ? AppResources.PIN : AppResources.MasterPassword;
|
MasterPasswordCell.Label.Text = _pinEnabled ? AppResources.PIN : AppResources.MasterPassword;
|
||||||
MasterPasswordCell.TextField.SecureTextEntry = true;
|
MasterPasswordCell.TextField.SecureTextEntry = true;
|
||||||
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
||||||
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
||||||
@@ -158,7 +162,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
CheckPasswordAsync().FireAndForget();
|
CheckPasswordAsync().FireAndForget();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.TextField.KeyboardType = UIKeyboardType.NumberPad;
|
MasterPasswordCell.TextField.KeyboardType = UIKeyboardType.NumberPad;
|
||||||
}
|
}
|
||||||
@@ -177,7 +181,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
base.ViewDidLoad();
|
base.ViewDidLoad();
|
||||||
|
|
||||||
if (_biometricLock)
|
if (_biometricEnabled)
|
||||||
{
|
{
|
||||||
if (!_biometricIntegrityValid)
|
if (!_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
@@ -198,18 +202,18 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
if (_usesKeyConnector)
|
if (_usesKeyConnector)
|
||||||
{
|
{
|
||||||
if (!(_pinLock || _biometricLock) ||
|
if (!(_pinEnabled || _biometricEnabled) ||
|
||||||
(_biometricLock && !_biometricIntegrityValid))
|
(_biometricEnabled && !_biometricIntegrityValid))
|
||||||
{
|
{
|
||||||
PromptSSO();
|
PromptSSO();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!_biometricLock || !_biometricIntegrityValid)
|
else if (!_biometricEnabled || !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.TextField.BecomeFirstResponder();
|
MasterPasswordCell.TextField.BecomeFirstResponder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task CheckPasswordAsync()
|
protected async Task CheckPasswordAsync()
|
||||||
{
|
{
|
||||||
if (_checkingPassword)
|
if (_checkingPassword)
|
||||||
@@ -224,7 +228,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
{
|
{
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(AppResources.ValidationFieldRequired,
|
string.Format(AppResources.ValidationFieldRequired,
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword),
|
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword),
|
||||||
AppResources.Ok);
|
AppResources.Ok);
|
||||||
PresentViewController(alert, true, null);
|
PresentViewController(alert, true, null);
|
||||||
return;
|
return;
|
||||||
@@ -246,33 +250,53 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
var failed = true;
|
var failed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
EncString userKeyPin = null;
|
||||||
|
EncString oldPinProtected = null;
|
||||||
|
if (_pinStatus == PinLockEnum.Persistent)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
userKeyPin = await _stateService.GetUserKeyPinAsync();
|
||||||
|
var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
|
||||||
|
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
|
||||||
|
}
|
||||||
|
else if (_pinStatus == PinLockEnum.Transient)
|
||||||
|
{
|
||||||
|
userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync();
|
||||||
|
oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserKey userKey;
|
||||||
|
if (oldPinProtected != null)
|
||||||
|
{
|
||||||
|
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
|
||||||
|
_pinStatus == PinLockEnum.Transient,
|
||||||
|
inputtedValue,
|
||||||
|
email,
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
oldPinProtected
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
userKey = await _cryptoService.DecryptUserKeyWithPinAsync(
|
||||||
kdfConfig);
|
inputtedValue,
|
||||||
failed = false;
|
email,
|
||||||
|
kdfConfig,
|
||||||
|
userKeyPin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
var decryptedPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), userKey);
|
||||||
|
failed = decryptedPin != inputtedValue;
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(userKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -286,33 +310,27 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig);
|
var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig);
|
||||||
|
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedPasswordHash = await _cryptoService.GetPasswordHashAsync();
|
||||||
if (storedKeyHash == null)
|
if (storedPasswordHash == null)
|
||||||
{
|
{
|
||||||
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
||||||
if (key2.KeyB64 == oldKey)
|
if (masterKey.KeyB64 == oldKey)
|
||||||
{
|
{
|
||||||
var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization);
|
var localPasswordHash = await _cryptoService.HashPasswordAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization);
|
||||||
await _secureStorageService.RemoveAsync("oldKey");
|
await _secureStorageService.RemoveAsync("oldKey");
|
||||||
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
await _cryptoService.SetPasswordHashAsync(localPasswordHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, key2);
|
var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(inputtedValue, masterKey);
|
||||||
if (passwordValid)
|
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 AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2, true);
|
|
||||||
|
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
|
await SetKeyAndContinueAsync(userKey, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -339,12 +357,12 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
public async Task PromptBiometricAsync()
|
public async Task PromptBiometricAsync()
|
||||||
{
|
{
|
||||||
if (!_biometricLock || !_biometricIntegrityValid)
|
if (!_biometricEnabled || !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword,
|
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword,
|
||||||
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
||||||
await _stateService.SetBiometricLockedAsync(!success);
|
await _stateService.SetBiometricLockedAsync(!success);
|
||||||
if (success)
|
if (success)
|
||||||
@@ -371,12 +389,12 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
PresentViewController(loginController, true, null);
|
PresentViewController(loginController, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key, bool masterPassword = false)
|
private async Task SetKeyAndContinueAsync(UserKey userKey, bool masterPassword = false)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetUserKeyAsync(userKey);
|
||||||
}
|
}
|
||||||
DoContinue(masterPassword);
|
DoContinue(masterPassword);
|
||||||
}
|
}
|
||||||
@@ -396,7 +414,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private async Task EnableBiometricsIfNeeded()
|
private async Task EnableBiometricsIfNeeded()
|
||||||
{
|
{
|
||||||
// Re-enable biometrics if initial use
|
// Re-enable biometrics if initial use
|
||||||
if (_biometricLock & !_biometricIntegrityValid)
|
if (_biometricEnabled & !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
||||||
}
|
}
|
||||||
@@ -405,7 +423,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private void InvalidValue()
|
private void InvalidValue()
|
||||||
{
|
{
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(null, _pinLock ? AppResources.PIN : AppResources.InvalidMasterPassword),
|
string.Format(null, _pinEnabled ? AppResources.PIN : AppResources.InvalidMasterPassword),
|
||||||
AppResources.Ok, (a) =>
|
AppResources.Ok, (a) =>
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -490,7 +508,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (!controller._biometricUnlockOnly && controller._biometricLock) ||
|
return (!controller._biometricUnlockOnly && controller._biometricEnabled) ||
|
||||||
controller._passwordReprompt
|
controller._passwordReprompt
|
||||||
? 2
|
? 2
|
||||||
: 1;
|
: 1;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using Bit.Core.Enums;
|
|||||||
using Bit.App.Pages;
|
using Bit.App.Pages;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.iOS.Core.Controllers
|
namespace Bit.iOS.Core.Controllers
|
||||||
{
|
{
|
||||||
@@ -29,10 +30,9 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private IPlatformUtilsService _platformUtilsService;
|
private IPlatformUtilsService _platformUtilsService;
|
||||||
private IBiometricService _biometricService;
|
private IBiometricService _biometricService;
|
||||||
private IKeyConnectorService _keyConnectorService;
|
private IKeyConnectorService _keyConnectorService;
|
||||||
private bool _isPinProtected;
|
private PinLockEnum _pinStatus;
|
||||||
private bool _isPinProtectedWithKey;
|
private bool _pinEnabled;
|
||||||
private bool _pinLock;
|
private bool _biometricEnabled;
|
||||||
private bool _biometricLock;
|
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
private bool _passwordReprompt = false;
|
private bool _passwordReprompt = false;
|
||||||
private bool _usesKeyConnector;
|
private bool _usesKeyConnector;
|
||||||
@@ -96,25 +96,28 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
|
if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync())
|
||||||
{
|
{
|
||||||
_passwordReprompt = true;
|
_passwordReprompt = true;
|
||||||
_isPinProtected = false;
|
_pinStatus = PinLockEnum.Disabled;
|
||||||
_isPinProtectedWithKey = false;
|
_pinEnabled = false;
|
||||||
_pinLock = false;
|
_biometricEnabled = false;
|
||||||
_biometricLock = false;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(_isPinProtected, _isPinProtectedWithKey) = await _vaultTimeoutService.IsPinLockSetAsync();
|
_pinStatus = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
_pinLock = (_isPinProtected && await _stateService.GetPinProtectedKeyAsync() != null) ||
|
|
||||||
_isPinProtectedWithKey;
|
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync()
|
||||||
_biometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() &&
|
?? await _stateService.GetPinProtectedKeyAsync();
|
||||||
await _cryptoService.HasKeyAsync();
|
_pinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) ||
|
||||||
|
_pinStatus == PinLockEnum.Persistent;
|
||||||
|
|
||||||
|
_biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync()
|
||||||
|
&& await _cryptoService.HasEncryptedUserKeyAsync();
|
||||||
_biometricIntegrityValid =
|
_biometricIntegrityValid =
|
||||||
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
await _platformUtilsService.IsBiometricIntegrityValidAsync(BiometricIntegritySourceKey);
|
||||||
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
_usesKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
||||||
_biometricUnlockOnly = _usesKeyConnector && _biometricLock && !_pinLock;
|
_biometricUnlockOnly = _usesKeyConnector && _biometricEnabled && !_pinEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
BaseNavItem.Title = AppResources.VerifyPIN;
|
BaseNavItem.Title = AppResources.VerifyPIN;
|
||||||
}
|
}
|
||||||
@@ -143,7 +146,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
if (!_biometricUnlockOnly)
|
if (!_biometricUnlockOnly)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.Label.Text = _pinLock ? AppResources.PIN : AppResources.MasterPassword;
|
MasterPasswordCell.Label.Text = _pinEnabled ? AppResources.PIN : AppResources.MasterPassword;
|
||||||
MasterPasswordCell.TextField.SecureTextEntry = true;
|
MasterPasswordCell.TextField.SecureTextEntry = true;
|
||||||
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go;
|
||||||
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) =>
|
||||||
@@ -151,7 +154,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
CheckPasswordAsync().GetAwaiter().GetResult();
|
CheckPasswordAsync().GetAwaiter().GetResult();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.TextField.KeyboardType = UIKeyboardType.NumberPad;
|
MasterPasswordCell.TextField.KeyboardType = UIKeyboardType.NumberPad;
|
||||||
}
|
}
|
||||||
@@ -165,7 +168,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
base.ViewDidLoad();
|
base.ViewDidLoad();
|
||||||
|
|
||||||
if (_biometricLock)
|
if (_biometricEnabled)
|
||||||
{
|
{
|
||||||
if (!_biometricIntegrityValid)
|
if (!_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
@@ -186,13 +189,13 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
// Users with key connector and without biometric or pin has no MP to unlock with
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
if (_usesKeyConnector)
|
if (_usesKeyConnector)
|
||||||
{
|
{
|
||||||
if (!(_pinLock || _biometricLock) ||
|
if (!(_pinEnabled || _biometricEnabled) ||
|
||||||
(_biometricLock && !_biometricIntegrityValid))
|
(_biometricEnabled && !_biometricIntegrityValid))
|
||||||
{
|
{
|
||||||
PromptSSO();
|
PromptSSO();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!_biometricLock || !_biometricIntegrityValid)
|
else if (!_biometricEnabled || !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
MasterPasswordCell.TextField.BecomeFirstResponder();
|
MasterPasswordCell.TextField.BecomeFirstResponder();
|
||||||
}
|
}
|
||||||
@@ -204,7 +207,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
{
|
{
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(AppResources.ValidationFieldRequired,
|
string.Format(AppResources.ValidationFieldRequired,
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword),
|
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword),
|
||||||
AppResources.Ok);
|
AppResources.Ok);
|
||||||
PresentViewController(alert, true, null);
|
PresentViewController(alert, true, null);
|
||||||
return;
|
return;
|
||||||
@@ -214,33 +217,53 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
var inputtedValue = MasterPasswordCell.TextField.Text;
|
var inputtedValue = MasterPasswordCell.TextField.Text;
|
||||||
|
|
||||||
if (_pinLock)
|
if (_pinEnabled)
|
||||||
{
|
{
|
||||||
var failed = true;
|
var failed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isPinProtected)
|
EncString userKeyPin = null;
|
||||||
|
EncString oldPinProtected = null;
|
||||||
|
if (_pinStatus == PinLockEnum.Persistent)
|
||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
userKeyPin = await _stateService.GetUserKeyPinAsync();
|
||||||
|
var oldEncryptedKey = await _stateService.GetPinProtectedAsync();
|
||||||
|
oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null;
|
||||||
|
}
|
||||||
|
else if (_pinStatus == PinLockEnum.Transient)
|
||||||
|
{
|
||||||
|
userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync();
|
||||||
|
oldPinProtected = await _stateService.GetPinProtectedKeyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
UserKey userKey;
|
||||||
|
if (oldPinProtected != null)
|
||||||
|
{
|
||||||
|
userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync(
|
||||||
|
_pinStatus == PinLockEnum.Transient,
|
||||||
|
inputtedValue,
|
||||||
|
email,
|
||||||
kdfConfig,
|
kdfConfig,
|
||||||
await _stateService.GetPinProtectedKeyAsync());
|
oldPinProtected
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email,
|
userKey = await _cryptoService.DecryptUserKeyWithPinAsync(
|
||||||
kdfConfig);
|
inputtedValue,
|
||||||
failed = false;
|
email,
|
||||||
|
kdfConfig,
|
||||||
|
userKeyPin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
|
var decryptedPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), userKey);
|
||||||
|
failed = decryptedPin != inputtedValue;
|
||||||
|
if (!failed)
|
||||||
|
{
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(userKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -260,33 +283,27 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig);
|
var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig);
|
||||||
|
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedPasswordHash = await _cryptoService.GetPasswordHashAsync();
|
||||||
if (storedKeyHash == null)
|
if (storedPasswordHash == null)
|
||||||
{
|
{
|
||||||
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
var oldKey = await _secureStorageService.GetAsync<string>("oldKey");
|
||||||
if (key2.KeyB64 == oldKey)
|
if (masterKey.KeyB64 == oldKey)
|
||||||
{
|
{
|
||||||
var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization);
|
var localPasswordHash = await _cryptoService.HashPasswordAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization);
|
||||||
await _secureStorageService.RemoveAsync("oldKey");
|
await _secureStorageService.RemoveAsync("oldKey");
|
||||||
await _cryptoService.SetKeyHashAsync(localKeyHash);
|
await _cryptoService.SetPasswordHashAsync(localPasswordHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, key2);
|
var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(inputtedValue, masterKey);
|
||||||
if (passwordValid)
|
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 AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2, true);
|
|
||||||
|
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
|
await SetKeyAndContinueAsync(userKey, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -303,12 +320,12 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
public async Task PromptBiometricAsync()
|
public async Task PromptBiometricAsync()
|
||||||
{
|
{
|
||||||
if (!_biometricLock || !_biometricIntegrityValid)
|
if (!_biometricEnabled || !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword,
|
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword,
|
||||||
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
||||||
await _stateService.SetBiometricLockedAsync(!success);
|
await _stateService.SetBiometricLockedAsync(!success);
|
||||||
if (success)
|
if (success)
|
||||||
@@ -335,12 +352,12 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
PresentViewController(loginController, true, null);
|
PresentViewController(loginController, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key, bool masterPassword = false)
|
private async Task SetKeyAndContinueAsync(UserKey userKey, bool masterPassword = false)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetUserKeyAsync(userKey);
|
||||||
}
|
}
|
||||||
DoContinue(masterPassword);
|
DoContinue(masterPassword);
|
||||||
}
|
}
|
||||||
@@ -360,7 +377,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private async Task EnableBiometricsIfNeeded()
|
private async Task EnableBiometricsIfNeeded()
|
||||||
{
|
{
|
||||||
// Re-enable biometrics if initial use
|
// Re-enable biometrics if initial use
|
||||||
if (_biometricLock & !_biometricIntegrityValid)
|
if (_biometricEnabled & !_biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
await _biometricService.SetupBiometricAsync(BiometricIntegritySourceKey);
|
||||||
}
|
}
|
||||||
@@ -369,7 +386,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
private void InvalidValue()
|
private void InvalidValue()
|
||||||
{
|
{
|
||||||
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred,
|
||||||
string.Format(null, _pinLock ? AppResources.PIN : AppResources.InvalidMasterPassword),
|
string.Format(null, _pinEnabled ? AppResources.PIN : AppResources.InvalidMasterPassword),
|
||||||
AppResources.Ok, (a) =>
|
AppResources.Ok, (a) =>
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -444,7 +461,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
|
|
||||||
public override nint NumberOfSections(UITableView tableView)
|
public override nint NumberOfSections(UITableView tableView)
|
||||||
{
|
{
|
||||||
return (!_controller._biometricUnlockOnly && _controller._biometricLock) ||
|
return (!_controller._biometricUnlockOnly && _controller._biometricEnabled) ||
|
||||||
_controller._passwordReprompt
|
_controller._passwordReprompt
|
||||||
? 2
|
? 2
|
||||||
: 1;
|
: 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user