diff --git a/src/App/App.xaml.cs b/src/App/App.xaml.cs index d84009547..1585bad42 100644 --- a/src/App/App.xaml.cs +++ b/src/App/App.xaml.cs @@ -239,7 +239,6 @@ namespace Bit.App _passwordGenerationService.ClearAsync(), _lockService.ClearAsync(), _stateService.PurgeAsync()); - _lockService.PinLocked = false; _lockService.FingerprintLocked = true; _searchService.ClearIndex(); _authService.LogOut(() => diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 317aee4df..73b7008a9 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -25,7 +25,6 @@ namespace Bit.App.Pages private readonly IEnvironmentService _environmentService; private readonly IStateService _stateService; - private bool _hasKey; private string _email; private bool _showPassword; private bool _pinLock; @@ -104,8 +103,7 @@ namespace Bit.App.Pages public async Task InitAsync(bool autoPromptFingerprint) { _pinSet = await _lockService.IsPinLockSetAsync(); - _hasKey = await _cryptoService.HasKeyAsync(); - PinLock = (_pinSet.Item1 && _hasKey) || _pinSet.Item2; + PinLock = (_pinSet.Item1 && _lockService.PinProtectedKey != null) || _pinSet.Item2; FingerprintLock = await _lockService.IsFingerprintLockSetAsync(); _email = await _userService.GetEmailAsync(); var webVault = _environmentService.GetWebVaultUrl(); @@ -169,14 +167,17 @@ namespace Bit.App.Pages { if(_pinSet.Item1) { + var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, + kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000), + _lockService.PinProtectedKey); + var encKey = await _cryptoService.GetEncKeyAsync(key); var protectedPin = await _storageService.GetAsync(Constants.ProtectedPin); - var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin)); + var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey); failed = decPin != Pin; - _lockService.PinLocked = failed; if(!failed) { Pin = string.Empty; - await DoContinueAsync(); + await SetKeyAndContinueAsync(key); } } else @@ -221,6 +222,15 @@ namespace Bit.App.Pages } if(storedKeyHash != null && keyHash != null && storedKeyHash == keyHash) { + if(_pinSet.Item1) + { + var protectedPin = await _storageService.GetAsync(Constants.ProtectedPin); + var encKey = await _cryptoService.GetEncKeyAsync(key); + var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey); + var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, + kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000)); + _lockService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey); + } MasterPassword = string.Empty; await SetKeyAndContinueAsync(key); } @@ -278,7 +288,8 @@ namespace Bit.App.Pages private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key) { - if(!_hasKey) + var hasKey = await _cryptoService.HasKeyAsync(); + if(!hasKey) { await _cryptoService.SetKeyAsync(key); } @@ -287,7 +298,6 @@ namespace Bit.App.Pages private async Task DoContinueAsync() { - _lockService.PinLocked = false; _lockService.FingerprintLocked = false; var disableFavicon = await _storageService.GetAsync(Constants.DisableFaviconKey); await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault()); diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index 3c172522a..43d0e3eb6 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -214,21 +214,24 @@ namespace Bit.App.Pages var masterPassOnRestart = await _platformUtilsService.ShowDialogAsync( AppResources.PINRequireMasterPasswordRestart, AppResources.UnlockWithPIN, AppResources.Yes, AppResources.No); + + var kdf = await _userService.GetKdfAsync(); + var kdfIterations = await _userService.GetKdfIterationsAsync(); + var email = await _userService.GetEmailAsync(); + var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, + kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256), + kdfIterations.GetValueOrDefault(5000)); + var key = await _cryptoService.GetKeyAsync(); + var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey); + if(masterPassOnRestart) { var encPin = await _cryptoService.EncryptAsync(pin); await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString); + _lockService.PinProtectedKey = pinProtectedKey; } else { - var kdf = await _userService.GetKdfAsync(); - var kdfIterations = await _userService.GetKdfIterationsAsync(); - var email = await _userService.GetEmailAsync(); - var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, - kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256), - kdfIterations.GetValueOrDefault(5000)); - var key = await _cryptoService.GetKeyAsync(); - var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey); await _storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString); } } @@ -239,8 +242,8 @@ namespace Bit.App.Pages } if(!_pin) { - await _storageService.RemoveAsync(Constants.PinProtectedKey); - await _storageService.RemoveAsync(Constants.ProtectedPin); + await _cryptoService.ClearPinProtectedKeyAsync(); + await _lockService.ClearAsync(); } BuildList(); } diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 6df2cc9f2..6bcfbebcc 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -22,7 +22,7 @@ namespace Bit.Core.Abstractions Task EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null); Task EncryptAsync(string plainValue, SymmetricCryptoKey key = null); Task EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null); - Task GetEncKeyAsync(); + Task GetEncKeyAsync(SymmetricCryptoKey key = null); Task> GetFingerprintAsync(string userId, byte[] publicKey = null); Task GetKeyAsync(); Task GetKeyHashAsync(); @@ -35,7 +35,8 @@ namespace Bit.Core.Abstractions Task HasKeyAsync(); Task> MakeEncKeyAsync(SymmetricCryptoKey key); Task MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations); - Task MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations); + Task MakeKeyFromPinAsync(string pin, string salt, KdfType kdf, int kdfIterations, + CipherString protectedKeyCs = null); Task> MakeKeyPairAsync(SymmetricCryptoKey key = null); Task MakePinKeyAysnc(string pin, string salt, KdfType kdf, int kdfIterations); Task> MakeShareKeyAsync(); @@ -49,4 +50,4 @@ namespace Bit.Core.Abstractions Task SetOrgKeysAsync(IEnumerable orgs); Task ToggleKeyAsync(); } -} \ No newline at end of file +} diff --git a/src/Core/Abstractions/ILockService.cs b/src/Core/Abstractions/ILockService.cs index 43e7b0270..dc2820b66 100644 --- a/src/Core/Abstractions/ILockService.cs +++ b/src/Core/Abstractions/ILockService.cs @@ -1,11 +1,12 @@ using System; using System.Threading.Tasks; +using Bit.Core.Models.Domain; namespace Bit.Core.Abstractions { public interface ILockService { - bool PinLocked { get; set; } + CipherString PinProtectedKey { get; set; } bool FingerprintLocked { get; set; } Task CheckLockAsync(); @@ -16,4 +17,4 @@ namespace Bit.Core.Abstractions Task LockAsync(bool allowSoftLock = false, bool userInitiated = false); Task SetLockOptionAsync(int? lockOption); } -} \ No newline at end of file +} diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index b79a608cc..c3a54df8b 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -118,7 +118,7 @@ namespace Bit.Core.Services return _keyHash; } - public Task GetEncKeyAsync() + public Task GetEncKeyAsync(SymmetricCryptoKey key = null) { if(_encKey != null) { @@ -138,7 +138,10 @@ namespace Bit.Core.Services return null; } - var key = await GetKeyAsync(); + if(key == null) + { + key = await GetKeyAsync(); + } if(key == null) { return null; @@ -386,14 +389,17 @@ namespace Bit.Core.Services } public async Task MakeKeyFromPinAsync(string pin, string salt, - KdfType kdf, int kdfIterations) + KdfType kdf, int kdfIterations, CipherString protectedKeyCs = null) { - var pinProtectedKey = await _storageService.GetAsync(Constants.PinProtectedKey); - if(pinProtectedKey == null) + if(protectedKeyCs == null) { - throw new Exception("No PIN protected key found."); + var pinProtectedKey = await _storageService.GetAsync(Constants.PinProtectedKey); + if(pinProtectedKey == null) + { + throw new Exception("No PIN protected key found."); + } + protectedKeyCs = new CipherString(pinProtectedKey); } - var protectedKeyCs = new CipherString(pinProtectedKey); var pinKey = await MakePinKeyAysnc(pin, salt, kdf, kdfIterations); var decKey = await DecryptToBytesAsync(protectedKeyCs, pinKey); return new SymmetricCryptoKey(decKey); diff --git a/src/Core/Services/LockService.cs b/src/Core/Services/LockService.cs index a264a24bf..a06b116b0 100644 --- a/src/Core/Services/LockService.cs +++ b/src/Core/Services/LockService.cs @@ -1,4 +1,5 @@ using Bit.Core.Abstractions; +using Bit.Core.Models.Domain; using System; using System.Threading.Tasks; @@ -41,7 +42,7 @@ namespace Bit.Core.Services _lockedCallback = lockedCallback; } - public bool PinLocked { get; set; } + public CipherString PinProtectedKey { get; set; } = null; public bool FingerprintLocked { get; set; } = true; public async Task IsLockedAsync() @@ -49,18 +50,11 @@ namespace Bit.Core.Services var hasKey = await _cryptoService.HasKeyAsync(); if(hasKey) { - if(PinLocked) + var fingerprintSet = await IsFingerprintLockSetAsync(); + if(fingerprintSet && FingerprintLocked) { return true; } - else - { - var fingerprintSet = await IsFingerprintLockSetAsync(); - if(fingerprintSet && FingerprintLocked) - { - return true; - } - } } return !hasKey; } @@ -111,13 +105,8 @@ namespace Bit.Core.Services } if(allowSoftLock) { - var pinSet = await IsPinLockSetAsync(); - if(pinSet.Item1) - { - PinLocked = true; - } FingerprintLocked = await IsFingerprintLockSetAsync(); - if(FingerprintLocked || PinLocked) + if(FingerprintLocked) { _messagingService.Send("locked", userInitiated); _lockedCallback?.Invoke(userInitiated); @@ -159,6 +148,7 @@ namespace Bit.Core.Services public async Task ClearAsync() { + PinProtectedKey = null; await _storageService.RemoveAsync(Constants.ProtectedPin); } } diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs index 3bc69aab4..ad0c17ce7 100644 --- a/src/iOS.Core/Controllers/LockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -23,7 +23,6 @@ namespace Bit.iOS.Core.Controllers private IStorageService _secureStorageService; private IPlatformUtilsService _platformUtilsService; private Tuple _pinSet; - private bool _hasKey; private bool _pinLock; private bool _fingerprintLock; private int _invalidPinAttempts; @@ -52,8 +51,7 @@ namespace Bit.iOS.Core.Controllers _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _pinSet = _lockService.IsPinLockSetAsync().GetAwaiter().GetResult(); - _hasKey = _cryptoService.HasKeyAsync().GetAwaiter().GetResult(); - _pinLock = (_pinSet.Item1 && _hasKey) || _pinSet.Item2; + _pinLock = (_pinSet.Item1 && _lockService.PinProtectedKey != null) || _pinSet.Item2; _fingerprintLock = _lockService.IsFingerprintLockSetAsync().GetAwaiter().GetResult(); BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword; @@ -125,13 +123,16 @@ namespace Bit.iOS.Core.Controllers { if(_pinSet.Item1) { + var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email, + kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000), + _lockService.PinProtectedKey); + var encKey = await _cryptoService.GetEncKeyAsync(key); var protectedPin = await _storageService.GetAsync(Bit.Core.Constants.ProtectedPin); - var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin)); + var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey); failed = decPin != inputtedValue; - _lockService.PinLocked = failed; if(!failed) { - DoContinue(); + await SetKeyAndContinueAsync(key); } } else @@ -174,6 +175,15 @@ namespace Bit.iOS.Core.Controllers } if(storedKeyHash != null && keyHash != null && storedKeyHash == keyHash) { + if(_pinSet.Item1) + { + var protectedPin = await _storageService.GetAsync(Bit.Core.Constants.ProtectedPin); + var encKey = await _cryptoService.GetEncKeyAsync(key2); + var decPin = await _cryptoService.DecryptToUtf8Async(new CipherString(protectedPin), encKey); + var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email, + kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000)); + _lockService.PinProtectedKey = await _cryptoService.EncryptAsync(key2.Key, pinKey); + } await SetKeyAndContinueAsync(key2); } else @@ -185,7 +195,8 @@ namespace Bit.iOS.Core.Controllers private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key) { - if(!_hasKey) + var hasKey = await _cryptoService.HasKeyAsync(); + if(!hasKey) { await _cryptoService.SetKeyAsync(key); } @@ -194,7 +205,6 @@ namespace Bit.iOS.Core.Controllers private void DoContinue() { - _lockService.PinLocked = false; _lockService.FingerprintLocked = false; MasterPasswordCell.TextField.ResignFirstResponder(); Success();