diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 529e6e835..99e11a629 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -9,7 +9,7 @@ namespace Bit.Core.Abstractions { public interface ICryptoService { - Task SetUserKeyAsync(UserKey userKey); + Task SetUserKeyAsync(UserKey userKey, string userId = null); Task GetUserKeyAsync(string userId = null); Task HasUserKeyAsync(string userId = null); Task MakeUserKeyAsync(); diff --git a/src/Core/Abstractions/IStateService.cs b/src/Core/Abstractions/IStateService.cs index d6f93e021..ec40dd5ef 100644 --- a/src/Core/Abstractions/IStateService.cs +++ b/src/Core/Abstractions/IStateService.cs @@ -44,18 +44,18 @@ namespace Bit.Core.Abstractions Task CanAccessPremiumAsync(string userId = null); Task GetProtectedPinAsync(string userId = null); Task SetPersonalPremiumAsync(bool value, string userId = null); - Task GetUserKeyPin(string userId = null); - Task SetUserKeyPin(string value, string userId = null); - Task GetUserKeyPinEphemeral(string userId = null); - Task SetUserKeyPinEphemeral(EncString value, string userId = null); + Task GetUserKeyPinAsync(string userId = null); + Task SetUserKeyPinAsync(EncString value, string userId = null); + Task GetUserKeyPinEphemeralAsync(string userId = null); + Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null); Task SetProtectedPinAsync(string value, string userId = null); - [Obsolete("Use GetUserKeyPin instead, left for migration purposes")] + [Obsolete("Use GetUserKeyPinAsync instead, left for migration purposes")] Task GetPinProtectedAsync(string userId = null); - [Obsolete("Use SetUserKeyPin instead")] + [Obsolete("Use SetUserKeyPinAsync instead")] Task SetPinProtectedAsync(string value, string userId = null); - [Obsolete("Use GetUserKeyPinEphemeral instead, left for migration purposes")] + [Obsolete("Use GetUserKeyPinEphemeralAsync instead, left for migration purposes")] Task GetPinProtectedKeyAsync(string userId = null); - [Obsolete("Use SetUserKeyPinEphemeral instead")] + [Obsolete("Use SetUserKeyPinEphemeralAsync instead")] Task SetPinProtectedKeyAsync(EncString value, string userId = null); Task SetKdfConfigurationAsync(KdfConfig config, string userId = null); Task GetKeyHashAsync(string userId = null); diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 50a50b48d..f2da0299e 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -36,9 +36,15 @@ namespace Bit.Core.Services _cryptoFunctionService = cryptoFunctionService; } - public async Task SetUserKeyAsync(UserKey userKey) + public async Task SetUserKeyAsync(UserKey userKey, string userId = null) { - await _stateService.SetUserKeyAsync(userKey); + await _stateService.SetUserKeyAsync(userKey, userId); + + // Refresh the Pin Key if the user has a Pin set + if (await _stateService.GetProtectedPinAsync(userId) != null) + { + await StorePinKey(userKey, userId); + } } public async Task GetUserKeyAsync(string userId = null) @@ -610,6 +616,25 @@ namespace Bit.Core.Services // Helpers + private async Task StorePinKey(UserKey userKey, string userId = null) + { + var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId))); + var pinKey = await MakePinKeyAsync( + pin, + await _stateService.GetEmailAsync(userId), + await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile)) + ); + var encPin = await EncryptAsync(userKey.Key, pinKey); + + // TODO(Jake): Does this logic make sense? Should we save something in state to indicate the preference? + if (await _stateService.GetUserKeyPinAsync(userId) != null) + { + await _stateService.SetUserKeyPinAsync(encPin, userId); + return; + } + await _stateService.SetUserKeyPinEphemeralAsync(encPin, userId); + } + private async Task AesEncryptAsync(byte[] data, SymmetricCryptoKey key) { var obj = new EncryptedObject diff --git a/src/Core/Services/StateService.cs b/src/Core/Services/StateService.cs index 9f03e62c1..7eb54919c 100644 --- a/src/Core/Services/StateService.cs +++ b/src/Core/Services/StateService.cs @@ -396,25 +396,25 @@ namespace Bit.Core.Services } // TODO(Jake): Does this need to be secure storage? - public async Task GetUserKeyPin(string userId = null) + public async Task GetUserKeyPinAsync(string userId = null) { return await _storageMediatorService.GetAsync(Constants.UserKeyPinKey(userId), false); } // TODO(Jake): Does this need to be secure storage? - public async Task SetUserKeyPin(string value, string userId = null) + public async Task SetUserKeyPinAsync(EncString value, string userId = null) { - await _storageMediatorService.SaveAsync(Constants.UserKeyPinKey(userId), value, false); + await _storageMediatorService.SaveAsync(Constants.UserKeyPinKey(userId), value.EncryptedString, false); } - public async Task GetUserKeyPinEphemeral(string userId = null) + public async Task GetUserKeyPinEphemeralAsync(string userId = null) { return (await GetAccountAsync( ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()) ))?.VolatileData?.UserKeyPinEphemeral; } - public async Task SetUserKeyPinEphemeral(EncString value, string userId = null) + public async Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()); @@ -423,7 +423,7 @@ namespace Bit.Core.Services await SaveAccountAsync(account, reconciledOptions); } - [Obsolete("Use GetUserKeyPin instead, left for migration purposes")] + [Obsolete("Use GetUserKeyPinAsync instead, left for migration purposes")] public async Task GetPinProtectedAsync(string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, @@ -431,7 +431,7 @@ namespace Bit.Core.Services return await GetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), reconciledOptions); } - [Obsolete("Use SetUserKeyPin instead")] + [Obsolete("Use SetUserKeyPinAsync instead")] public async Task SetPinProtectedAsync(string value, string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, @@ -439,7 +439,7 @@ namespace Bit.Core.Services await SetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), value, reconciledOptions); } - [Obsolete("Use GetUserKeyPinEphemeral instead, left for migration purposes")] + [Obsolete("Use GetUserKeyPinEphemeralAsync instead, left for migration purposes")] public async Task GetPinProtectedKeyAsync(string userId = null) { return (await GetAccountAsync( @@ -447,7 +447,7 @@ namespace Bit.Core.Services ))?.VolatileData?.PinProtectedKey; } - [Obsolete("Use SetUserKeyPinEphemeral instead")] + [Obsolete("Use SetUserKeyPinEphemeralAsync instead")] public async Task SetPinProtectedKeyAsync(EncString value, string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },