From 954de743f580fc90bb6ae585cbe40ce5eebbb063 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 15 May 2019 14:22:28 -0400 Subject: [PATCH] lock service --- src/Core/Abstractions/ILockService.cs | 17 ++++ src/Core/Constants.cs | 2 + src/Core/Services/LockService.cs | 139 ++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 src/Core/Abstractions/ILockService.cs create mode 100644 src/Core/Services/LockService.cs diff --git a/src/Core/Abstractions/ILockService.cs b/src/Core/Abstractions/ILockService.cs new file mode 100644 index 000000000..00e85966a --- /dev/null +++ b/src/Core/Abstractions/ILockService.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; + +namespace Bit.Core.Abstractions +{ + public interface ILockService + { + bool PinLocked { get; set; } + + Task CheckLockAsync(); + Task ClearAsync(); + Task IsLockedAsync(); + Task> IsPinLockSetAsync(); + Task LockAsync(bool allowSoftLock = false); + Task SetLockOptionAsync(int lockOption); + } +} \ No newline at end of file diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index e09205908..6f6c0af98 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -5,6 +5,8 @@ public const string AndroidAppProtocol = "androidapp://"; public const string iOSAppProtocol = "iosapp://"; public static string LockOptionKey = "lockOption"; + public static string LastActiveKey = "lastActive"; + public static string ProtectedPin = "protectedPin"; public static string PinProtectedKey = "pinProtectedKey"; public static string DefaultUriMatch = "defaultUriMatch"; public static string DisableAutoTotpCopyKey = "disableAutoTotpCopy"; diff --git a/src/Core/Services/LockService.cs b/src/Core/Services/LockService.cs new file mode 100644 index 000000000..7a0b61c9b --- /dev/null +++ b/src/Core/Services/LockService.cs @@ -0,0 +1,139 @@ +using Bit.Core.Abstractions; +using System; +using System.Threading.Tasks; + +namespace Bit.Core.Services +{ + public class LockService : ILockService + { + private readonly ICryptoService _cryptoService; + private readonly IUserService _userService; + private readonly IPlatformUtilsService _platformUtilsService; + private readonly IStorageService _storageService; + private readonly IFolderService _folderService; + private readonly ICipherService _cipherService; + private readonly ICollectionService _collectionService; + private readonly ISearchService _searchService; + private readonly IMessagingService _messagingService; + + public LockService( + ICryptoService cryptoService, + IUserService userService, + IPlatformUtilsService platformUtilsService, + IStorageService storageService, + IFolderService folderService, + ICipherService cipherService, + ICollectionService collectionService, + ISearchService searchService, + IMessagingService messagingService) + { + _cryptoService = cryptoService; + _userService = userService; + _platformUtilsService = platformUtilsService; + _storageService = storageService; + _folderService = folderService; + _cipherService = cipherService; + _collectionService = collectionService; + _searchService = searchService; + _messagingService = messagingService; + } + + public bool PinLocked { get; set; } + + // TODO: init timer? + + public async Task IsLockedAsync() + { + var hasKey = await _cryptoService.HasKeyAsync(); + if(hasKey && PinLocked) + { + return true; + } + return !hasKey; + } + + public async Task CheckLockAsync() + { + if(false) // TODO: view is open? + { + return; + } + var authed = await _userService.IsAuthenticatedAsync(); + if(!authed) + { + return; + } + var lockOption = _platformUtilsService.LockTimeout(); + if(lockOption == null) + { + lockOption = await _storageService.GetAsync(Constants.LockOptionKey); + } + if(lockOption.GetValueOrDefault(-1) < 0) + { + return; + } + var lastActive = await _storageService.GetAsync(Constants.LastActiveKey); + if(lastActive == null) + { + return; + } + var lockOptionsSeconds = lockOption.Value * 60; + var diff = DateTime.UtcNow - lastActive.Value; + if(diff.TotalSeconds >= lockOptionsSeconds) + { + // need to lock now + await LockAsync(true); + } + } + + public async Task LockAsync(bool allowSoftLock = false) + { + var authed = await _userService.IsAuthenticatedAsync(); + if(!authed) + { + return; + } + if(allowSoftLock) + { + var pinSet = await IsPinLockSetAsync(); + if(pinSet.Item1) + { + PinLocked = true; + _messagingService.Send("locked"); + // TODO: locked callback? + return; + } + } + await Task.WhenAll( + _cryptoService.ClearKeyAsync(), + _cryptoService.ClearOrgKeysAsync(true), + _cryptoService.ClearKeyPairAsync(true), + _cryptoService.ClearEncKeyAsync(true)); + + _folderService.ClearCache(); + _cipherService.ClearCache(); + _collectionService.ClearCache(); + _searchService.ClearIndex(); + _messagingService.Send("locked"); + // TODO: locked callback? + } + + public async Task SetLockOptionAsync(int lockOption) + { + await _storageService.SaveAsync(Constants.LockOptionKey, lockOption); + await _cryptoService.ToggleKeyAsync(); + } + + public async Task> IsPinLockSetAsync() + { + var protectedPin = await _storageService.GetAsync(Constants.ProtectedPin); + var pinProtectedKey = await _storageService.GetAsync(Constants.PinProtectedKey); + return new Tuple(protectedPin != null, pinProtectedKey != null); + } + + public async Task ClearAsync() + { + await _storageService.RemoveAsync(Constants.ProtectedPin); + } + } +}