2019-05-16 00:37:59 +03:00
|
|
|
|
using Bit.App.Abstractions;
|
2019-05-17 23:36:29 +03:00
|
|
|
|
using Bit.App.Models;
|
2019-05-16 00:37:59 +03:00
|
|
|
|
using Bit.App.Resources;
|
|
|
|
|
using Bit.Core;
|
|
|
|
|
using Bit.Core.Abstractions;
|
|
|
|
|
using Bit.Core.Enums;
|
|
|
|
|
using Bit.Core.Models.Domain;
|
|
|
|
|
using Bit.Core.Utilities;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Xamarin.Forms;
|
|
|
|
|
|
|
|
|
|
namespace Bit.App.Pages
|
|
|
|
|
{
|
|
|
|
|
public class LockPageViewModel : BaseViewModel
|
|
|
|
|
{
|
|
|
|
|
private readonly IPlatformUtilsService _platformUtilsService;
|
|
|
|
|
private readonly IDeviceActionService _deviceActionService;
|
|
|
|
|
private readonly ILockService _lockService;
|
|
|
|
|
private readonly ICryptoService _cryptoService;
|
|
|
|
|
private readonly IStorageService _storageService;
|
|
|
|
|
private readonly IUserService _userService;
|
|
|
|
|
private readonly IMessagingService _messagingService;
|
|
|
|
|
|
2019-05-17 16:42:20 +03:00
|
|
|
|
private bool _hasKey;
|
2019-05-16 00:37:59 +03:00
|
|
|
|
private string _email;
|
|
|
|
|
private bool _showPassword;
|
|
|
|
|
private bool _pinLock;
|
2019-05-17 00:30:07 +03:00
|
|
|
|
private bool _fingerprintLock;
|
2019-05-17 16:42:20 +03:00
|
|
|
|
private string _fingerprintButtonText;
|
2019-05-17 17:01:45 +03:00
|
|
|
|
private string _loggedInAsText;
|
|
|
|
|
private string _lockedVerifyText;
|
2019-05-16 00:37:59 +03:00
|
|
|
|
private int _invalidPinAttempts = 0;
|
|
|
|
|
private Tuple<bool, bool> _pinSet;
|
|
|
|
|
|
|
|
|
|
public LockPageViewModel()
|
|
|
|
|
{
|
|
|
|
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
|
|
|
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
|
|
|
|
_lockService = ServiceContainer.Resolve<ILockService>("lockService");
|
|
|
|
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
|
|
|
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
|
|
|
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
|
|
|
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
|
|
|
|
|
|
|
|
PageTitle = AppResources.VerifyMasterPassword;
|
|
|
|
|
TogglePasswordCommand = new Command(TogglePassword);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ShowPassword
|
|
|
|
|
{
|
|
|
|
|
get => _showPassword;
|
|
|
|
|
set => SetProperty(ref _showPassword, value,
|
|
|
|
|
additionalPropertyNames: new string[]
|
|
|
|
|
{
|
|
|
|
|
nameof(ShowPasswordIcon)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool PinLock
|
|
|
|
|
{
|
|
|
|
|
get => _pinLock;
|
|
|
|
|
set => SetProperty(ref _pinLock, value);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 00:30:07 +03:00
|
|
|
|
public bool FingerprintLock
|
|
|
|
|
{
|
|
|
|
|
get => _fingerprintLock;
|
|
|
|
|
set => SetProperty(ref _fingerprintLock, value);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 16:42:20 +03:00
|
|
|
|
public string FingerprintButtonText
|
|
|
|
|
{
|
|
|
|
|
get => _fingerprintButtonText;
|
|
|
|
|
set => SetProperty(ref _fingerprintButtonText, value);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 17:01:45 +03:00
|
|
|
|
public string LoggedInAsText
|
|
|
|
|
{
|
|
|
|
|
get => _loggedInAsText;
|
|
|
|
|
set => SetProperty(ref _loggedInAsText, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string LockedVerifyText
|
|
|
|
|
{
|
|
|
|
|
get => _lockedVerifyText;
|
|
|
|
|
set => SetProperty(ref _lockedVerifyText, value);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-16 00:37:59 +03:00
|
|
|
|
public Command TogglePasswordCommand { get; }
|
|
|
|
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
|
|
|
|
public string MasterPassword { get; set; }
|
|
|
|
|
public string Pin { get; set; }
|
2019-05-17 23:36:29 +03:00
|
|
|
|
public Action UnlockedAction { get; set; }
|
2019-05-16 00:37:59 +03:00
|
|
|
|
|
|
|
|
|
public async Task InitAsync()
|
|
|
|
|
{
|
|
|
|
|
_pinSet = await _lockService.IsPinLockSetAsync();
|
2019-05-17 16:42:20 +03:00
|
|
|
|
_hasKey = await _cryptoService.HasKeyAsync();
|
|
|
|
|
PinLock = (_pinSet.Item1 && _hasKey) || _pinSet.Item2;
|
2019-05-17 00:30:07 +03:00
|
|
|
|
FingerprintLock = await _lockService.IsFingerprintLockSetAsync();
|
2019-05-16 00:37:59 +03:00
|
|
|
|
_email = await _userService.GetEmailAsync();
|
2019-05-17 17:01:45 +03:00
|
|
|
|
LoggedInAsText = string.Format(AppResources.LoggedInAs, _email);
|
|
|
|
|
if(PinLock)
|
|
|
|
|
{
|
|
|
|
|
PageTitle = AppResources.VerifyPIN;
|
|
|
|
|
LockedVerifyText = AppResources.VaultLockedPIN;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PageTitle = AppResources.VerifyMasterPassword;
|
|
|
|
|
LockedVerifyText = AppResources.VaultLockedMasterPassword;
|
|
|
|
|
}
|
2019-05-17 00:30:07 +03:00
|
|
|
|
|
|
|
|
|
if(FingerprintLock)
|
|
|
|
|
{
|
2019-05-17 16:46:36 +03:00
|
|
|
|
FingerprintButtonText = _deviceActionService.SupportsFaceId() ? AppResources.UseFaceIDToUnlock :
|
|
|
|
|
AppResources.UseFingerprintToUnlock;
|
2019-05-17 00:30:07 +03:00
|
|
|
|
var tasks = Task.Run(async () =>
|
|
|
|
|
{
|
|
|
|
|
await Task.Delay(500);
|
2019-05-17 16:42:20 +03:00
|
|
|
|
Device.BeginInvokeOnMainThread(async () => await PromptFingerprintAsync());
|
2019-05-17 00:30:07 +03:00
|
|
|
|
});
|
|
|
|
|
}
|
2019-05-16 00:37:59 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task SubmitAsync()
|
|
|
|
|
{
|
|
|
|
|
if(PinLock && string.IsNullOrWhiteSpace(Pin))
|
|
|
|
|
{
|
|
|
|
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
|
|
|
|
string.Format(AppResources.ValidationFieldRequired, AppResources.PIN),
|
|
|
|
|
AppResources.Ok);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(!PinLock && string.IsNullOrWhiteSpace(MasterPassword))
|
|
|
|
|
{
|
|
|
|
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
|
|
|
|
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
|
|
|
|
AppResources.Ok);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var kdf = await _userService.GetKdfAsync();
|
|
|
|
|
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
|
|
|
|
|
|
|
|
|
if(PinLock)
|
|
|
|
|
{
|
|
|
|
|
var failed = true;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if(_pinSet.Item1)
|
|
|
|
|
{
|
|
|
|
|
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
|
|
|
|
var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin));
|
|
|
|
|
failed = decPin != Pin;
|
|
|
|
|
_lockService.PinLocked = failed;
|
2019-05-16 22:54:21 +03:00
|
|
|
|
if(!failed)
|
2019-05-16 00:37:59 +03:00
|
|
|
|
{
|
|
|
|
|
DoContinue();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
|
|
|
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
|
|
|
|
failed = false;
|
|
|
|
|
await SetKeyAndContinueAsync(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
failed = true;
|
|
|
|
|
}
|
|
|
|
|
if(failed)
|
|
|
|
|
{
|
|
|
|
|
_invalidPinAttempts++;
|
|
|
|
|
if(_invalidPinAttempts >= 5)
|
|
|
|
|
{
|
|
|
|
|
_messagingService.Send("logout");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidPIN,
|
|
|
|
|
AppResources.AnErrorHasOccurred);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
|
|
|
|
|
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
|
|
|
|
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
|
|
|
|
if(storedKeyHash != null && keyHash != null && storedKeyHash == keyHash)
|
|
|
|
|
{
|
|
|
|
|
await SetKeyAndContinueAsync(key);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidMasterPassword,
|
|
|
|
|
AppResources.AnErrorHasOccurred);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task LogOutAsync()
|
|
|
|
|
{
|
|
|
|
|
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LogoutConfirmation,
|
|
|
|
|
AppResources.LogOut, AppResources.Yes, AppResources.Cancel);
|
|
|
|
|
if(confirmed)
|
|
|
|
|
{
|
|
|
|
|
_messagingService.Send("logout");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void TogglePassword()
|
|
|
|
|
{
|
|
|
|
|
ShowPassword = !ShowPassword;
|
|
|
|
|
var page = (Page as LockPage);
|
|
|
|
|
var entry = PinLock ? page.PinEntry : page.MasterPasswordEntry;
|
|
|
|
|
entry.Focus();
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 16:42:20 +03:00
|
|
|
|
public async Task PromptFingerprintAsync()
|
|
|
|
|
{
|
|
|
|
|
var success = await _platformUtilsService.AuthenticateFingerprintAsync(null,
|
|
|
|
|
PinLock ? AppResources.PIN : AppResources.MasterPassword, () =>
|
|
|
|
|
{
|
|
|
|
|
var page = Page as LockPage;
|
|
|
|
|
if(PinLock)
|
|
|
|
|
{
|
|
|
|
|
page.PinEntry.Focus();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
page.MasterPasswordEntry.Focus();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
_lockService.FingerprintLocked = !success;
|
|
|
|
|
if(success)
|
|
|
|
|
{
|
|
|
|
|
DoContinue();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-16 00:37:59 +03:00
|
|
|
|
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key)
|
|
|
|
|
{
|
2019-05-17 16:42:20 +03:00
|
|
|
|
if(!_hasKey)
|
|
|
|
|
{
|
|
|
|
|
await _cryptoService.SetKeyAsync(key);
|
|
|
|
|
}
|
2019-05-16 15:41:57 +03:00
|
|
|
|
DoContinue();
|
2019-05-16 00:37:59 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DoContinue()
|
|
|
|
|
{
|
|
|
|
|
_messagingService.Send("unlocked");
|
2019-05-17 23:36:29 +03:00
|
|
|
|
UnlockedAction?.Invoke();
|
2019-05-16 00:37:59 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|