From f8c9cde2ed72163ea4e8584c1f8ec1f552973b21 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 20 Jul 2023 11:17:57 -0400 Subject: [PATCH 01/25] [PM-2713] optimize async code in crypto service --- src/Core/Services/CryptoService.cs | 67 +++++++++++++++--------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 2a314c119..5b7733a42 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -70,9 +70,9 @@ namespace Bit.Core.Services } } - public async Task GetUserKeyAsync(string userId = null) + public Task GetUserKeyAsync(string userId = null) { - return await _stateService.GetUserKeyAsync(userId); + return _stateService.GetUserKeyAsync(userId); } public async Task GetUserKeyWithLegacySupportAsync(string userId = null) @@ -103,20 +103,19 @@ namespace Bit.Core.Services return new UserKey(await _cryptoFunctionService.RandomBytesAsync(64)); } - public async Task ClearUserKeyAsync(string userId = null) + public Task ClearUserKeyAsync(string userId = null) { - await _stateService.SetUserKeyAsync(null, userId); + return _stateService.SetUserKeyAsync(null, userId); } - public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null) + public Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null) { - await _stateService.SetUserKeyMasterKeyAsync(value, userId); + return _stateService.SetUserKeyMasterKeyAsync(value, userId); } - public async Task SetMasterKeyAsync(MasterKey masterKey, string userId = null) + public Task SetMasterKeyAsync(MasterKey masterKey, string userId = null) { - await _stateService.SetMasterKeyAsync(masterKey, userId); - + return _stateService.SetMasterKeyAsync(masterKey, userId); } public async Task GetMasterKeyAsync(string userId = null) @@ -134,14 +133,14 @@ namespace Bit.Core.Services return masterKey; } - public async Task MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig) + public Task MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig) { - return await MakeKeyAsync(password, email, kdfConfig, keyBytes => new MasterKey(keyBytes)); + return MakeKeyAsync(password, email, kdfConfig, keyBytes => new MasterKey(keyBytes)); } - public async Task ClearMasterKeyAsync(string userId = null) + public Task ClearMasterKeyAsync(string userId = null) { - await _stateService.SetMasterKeyAsync(null, userId); + return _stateService.SetMasterKeyAsync(null, userId); } public async Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null) @@ -228,10 +227,10 @@ namespace Bit.Core.Services return Convert.ToBase64String(hash); } - public async Task SetPasswordHashAsync(string keyHash) + public Task SetPasswordHashAsync(string keyHash) { _passwordHash = keyHash; - await _stateService.SetKeyHashAsync(keyHash); + return _stateService.SetKeyHashAsync(keyHash); } public async Task GetPasswordHashAsync() @@ -248,10 +247,10 @@ namespace Bit.Core.Services return _passwordHash; } - public async Task ClearPasswordHashAsync(string userId = null) + public Task ClearPasswordHashAsync(string userId = null) { _passwordHash = null; - await _stateService.SetKeyHashAsync(null, userId); + return _stateService.SetKeyHashAsync(null, userId); } // TODO(Jake): Uses Master Key @@ -277,11 +276,11 @@ namespace Bit.Core.Services return false; } - public async Task SetOrgKeysAsync(IEnumerable orgs) + public Task SetOrgKeysAsync(IEnumerable orgs) { var orgKeys = orgs.ToDictionary(org => org.Id, org => org.Key); _orgKeys = null; - await _stateService.SetOrgKeysEncryptedAsync(orgKeys); + return _stateService.SetOrgKeysEncryptedAsync(orgKeys); } public async Task GetOrgKeyAsync(string orgId) @@ -429,12 +428,13 @@ namespace Bit.Core.Services return await StretchKeyAsync(pinKey) as PinKey; } - public async Task ClearPinKeysAsync(string userId = null) + public Task ClearPinKeysAsync(string userId = null) { - await _stateService.SetUserKeyPinAsync(null, userId); - await _stateService.SetUserKeyPinEphemeralAsync(null, userId); - await _stateService.SetProtectedPinAsync(null, userId); - await clearDeprecatedPinKeysAsync(userId); + return Task.WhenAll( + _stateService.SetUserKeyPinAsync(null, userId), + _stateService.SetUserKeyPinEphemeralAsync(null, userId), + _stateService.SetProtectedPinAsync(null, userId), + clearDeprecatedPinKeysAsync(userId)); } public async Task DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null) @@ -618,27 +618,27 @@ namespace Bit.Core.Services return await AesDecryptToBytesAsync(encType, ctBytes, ivBytes, macBytes, key); } - public async Task DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null) + public Task DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null) { var iv = Convert.FromBase64String(encString.Iv); var data = Convert.FromBase64String(encString.Data); var mac = !string.IsNullOrWhiteSpace(encString.Mac) ? Convert.FromBase64String(encString.Mac) : null; - return await AesDecryptToBytesAsync(encString.EncryptionType, data, iv, mac, key); + return AesDecryptToBytesAsync(encString.EncryptionType, data, iv, mac, key); } - public async Task DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null) + public Task DecryptToUtf8Async(EncString encString, SymmetricCryptoKey key = null) { - return await AesDecryptToUtf8Async(encString.EncryptionType, encString.Data, + return AesDecryptToUtf8Async(encString.EncryptionType, encString.Data, encString.Iv, encString.Mac, key); } - public async Task EncryptAsync(string plainValue, SymmetricCryptoKey key = null) + public Task EncryptAsync(string plainValue, SymmetricCryptoKey key = null) { if (plainValue == null) { return null; } - return await EncryptAsync(Encoding.UTF8.GetBytes(plainValue), key); + return EncryptAsync(Encoding.UTF8.GetBytes(plainValue), key); } public async Task EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null) @@ -979,10 +979,11 @@ namespace Bit.Core.Services return userKey; } - public async Task clearDeprecatedPinKeysAsync(string userId = null) + public Task clearDeprecatedPinKeysAsync(string userId = null) { - await _stateService.SetPinProtectedAsync(null); - await _stateService.SetPinProtectedKeyAsync(null); + return Task.WhenAll( + _stateService.SetPinProtectedAsync(null, userId), + _stateService.SetPinProtectedKeyAsync(null, userId)); } } } From 0da3d259553771dc83d51732a17924be75462ceb Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 20 Jul 2023 12:30:02 -0400 Subject: [PATCH 02/25] [PM-2713] rename password hash to master key hash --- src/App/Pages/Accounts/LockPageViewModel.cs | 10 ++--- .../Pages/Accounts/RegisterPageViewModel.cs | 2 +- .../Accounts/SetPasswordPageViewModel.cs | 6 +-- .../UpdateTempPasswordPageViewModel.cs | 4 +- .../Services/MobilePasswordRepromptService.cs | 2 +- .../VerificationActionsFlowHelper.cs | 2 +- src/Core/Abstractions/ICryptoService.cs | 10 ++--- src/Core/Services/AuthService.cs | 10 ++--- src/Core/Services/CryptoService.cs | 44 +++++++++---------- src/Core/Services/UserVerificationService.cs | 2 +- 10 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 6bb3aef27..0a4d58c29 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -319,20 +319,20 @@ namespace Bit.App.Pages else { var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, _email, kdfConfig); - var storedKeyHash = await _cryptoService.GetPasswordHashAsync(); + var storedKeyHash = await _cryptoService.GetMasterKeyHashAsync(); var passwordValid = false; MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null; if (storedKeyHash != null) { // Offline unlock possible - passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(MasterPassword, masterKey); + passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, masterKey); } else { // Online unlock required await _deviceActionService.ShowLoadingAsync(AppResources.Loading); - var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey, HashPurpose.ServerAuthorization); + var keyHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey, HashPurpose.ServerAuthorization); var request = new PasswordVerificationRequest(); request.MasterPasswordHash = keyHash; @@ -341,8 +341,8 @@ namespace Bit.App.Pages var response = await _apiService.PostAccountVerifyPasswordAsync(request); enforcedMasterPasswordOptions = response.MasterPasswordPolicy; passwordValid = true; - var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey, HashPurpose.LocalAuthorization); - await _cryptoService.SetPasswordHashAsync(localKeyHash); + var localKeyHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey, HashPurpose.LocalAuthorization); + await _cryptoService.SetMasterKeyHashAsync(localKeyHash); } catch (Exception e) { diff --git a/src/App/Pages/Accounts/RegisterPageViewModel.cs b/src/App/Pages/Accounts/RegisterPageViewModel.cs index b742a65ba..52cca400c 100644 --- a/src/App/Pages/Accounts/RegisterPageViewModel.cs +++ b/src/App/Pages/Accounts/RegisterPageViewModel.cs @@ -182,7 +182,7 @@ namespace Bit.App.Pages newMasterKey, await _cryptoService.MakeUserKeyAsync() ); - var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey); + var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey); var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey); var request = new RegisterRequest { diff --git a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs index 57ffaac79..6a358cc99 100644 --- a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs +++ b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs @@ -166,8 +166,8 @@ namespace Bit.App.Pages var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null); var email = await _stateService.GetEmailAsync(); var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig); - var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization); - var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization); + var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization); + var localMasterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization); var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey, await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync()); @@ -197,7 +197,7 @@ namespace Bit.App.Pages await _apiService.SetPasswordAsync(request); await _stateService.SetKdfConfigurationAsync(kdfConfig); await _cryptoService.SetMasterKeyAsync(newMasterKey); - await _cryptoService.SetPasswordHashAsync(localMasterPasswordHash); + await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash); await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString); await _cryptoService.SetPrivateKeyAsync(keys.Item2.EncryptedString); diff --git a/src/App/Pages/Accounts/UpdateTempPasswordPageViewModel.cs b/src/App/Pages/Accounts/UpdateTempPasswordPageViewModel.cs index 7d8ec85f0..941736f08 100644 --- a/src/App/Pages/Accounts/UpdateTempPasswordPageViewModel.cs +++ b/src/App/Pages/Accounts/UpdateTempPasswordPageViewModel.cs @@ -95,7 +95,7 @@ namespace Bit.App.Pages // Create new master key and hash new password var masterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, email, kdfConfig); - var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, masterKey); + var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, masterKey); // Encrypt user key with new master key var (userKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(masterKey); @@ -155,7 +155,7 @@ namespace Bit.App.Pages private async Task UpdatePasswordAsync(string newMasterPasswordHash, string newEncKey) { - var currentPasswordHash = await _cryptoService.HashPasswordAsync(CurrentMasterPassword, null); + var currentPasswordHash = await _cryptoService.HashMasterKeyAsync(CurrentMasterPassword, null); var request = new PasswordRequest { diff --git a/src/App/Services/MobilePasswordRepromptService.cs b/src/App/Services/MobilePasswordRepromptService.cs index 460e9df58..28a8e5a86 100644 --- a/src/App/Services/MobilePasswordRepromptService.cs +++ b/src/App/Services/MobilePasswordRepromptService.cs @@ -38,7 +38,7 @@ namespace Bit.App.Services return false; }; - return await _cryptoService.CompareAndUpdatePasswordHashAsync(password, null); + return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null); } public async Task Enabled() diff --git a/src/App/Utilities/VerificationActionsFlowHelper.cs b/src/App/Utilities/VerificationActionsFlowHelper.cs index eaf3376e9..febaf798d 100644 --- a/src/App/Utilities/VerificationActionsFlowHelper.cs +++ b/src/App/Utilities/VerificationActionsFlowHelper.cs @@ -121,7 +121,7 @@ namespace Bit.App.Utilities } var parameters = GetParameters(); - parameters.Secret = await _cryptoService.HashPasswordAsync(password, null); + parameters.Secret = await _cryptoService.HashMasterKeyAsync(password, null); parameters.VerificationType = VerificationType.MasterPassword; await ExecuteAsync(parameters); break; diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 3a388172a..b91b22d26 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -27,11 +27,11 @@ namespace Bit.Core.Abstractions Task DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null); Task> MakeDataEncKeyAsync(UserKey key); Task> MakeDataEncKeyAsync(OrgKey key); - Task HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); - Task SetPasswordHashAsync(string keyHash); - Task GetPasswordHashAsync(); - Task ClearPasswordHashAsync(string userId = null); - Task CompareAndUpdatePasswordHashAsync(string masterPassword, MasterKey key); + Task HashMasterKeyAsync(string password, MasterKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); + Task SetMasterKeyHashAsync(string keyHash); + Task GetMasterKeyHashAsync(); + Task ClearMasterKeyHashAsync(string userId = null); + Task CompareAndUpdateKeyHashAsync(string masterPassword, MasterKey key); Task SetOrgKeysAsync(IEnumerable orgs); Task GetOrgKeyAsync(string orgId); Task> GetOrgKeysAsync(); diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index 0bf62e075..d12e8f914 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -145,8 +145,8 @@ namespace Bit.Core.Services SelectedTwoFactorProviderType = null; _2faForcePasswordResetReason = null; var key = await MakePreloginKeyAsync(masterPassword, email); - var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key); - var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization); + var hashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key); + var localHashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key, HashPurpose.LocalAuthorization); var result = await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, null, null, null, captchaToken); if (await RequirePasswordChangeAsync(email, masterPassword)) @@ -236,8 +236,8 @@ namespace Bit.Core.Services { SelectedTwoFactorProviderType = null; var key = await MakePreloginKeyAsync(masterPassword, email); - var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key); - var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization); + var hashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key); + var localHashedPassword = await _cryptoService.HashMasterKeyAsync(masterPassword, key, HashPurpose.LocalAuthorization); return await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, twoFactorProvider, twoFactorToken, remember); } @@ -473,7 +473,7 @@ namespace Bit.Core.Services if (localHashedPassword != null) { - await _cryptoService.SetPasswordHashAsync(localHashedPassword); + await _cryptoService.SetMasterKeyHashAsync(localHashedPassword); } if (code == null || tokenResponse.Key != null) diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 5b7733a42..b336b3d4e 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -20,7 +20,7 @@ namespace Bit.Core.Services private readonly ICryptoFunctionService _cryptoFunctionService; private SymmetricCryptoKey _legacyEtmKey; - private string _passwordHash; + private string _masterKeyHash; private byte[] _publicKey; private byte[] _privateKey; private Dictionary _orgKeys; @@ -37,7 +37,7 @@ namespace Bit.Core.Services public void ClearCache() { _legacyEtmKey = null; - _passwordHash = null; + _masterKeyHash = null; _publicKey = null; _privateKey = null; _orgKeys = null; @@ -211,64 +211,62 @@ namespace Bit.Core.Services return await BuildProtectedSymmetricKey(key, newSymKey); } - // TODO(Jake): Uses Master Key - public async Task HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization) + public async Task HashMasterKeyAsync(string password, MasterKey masterKey, HashPurpose hashPurpose = HashPurpose.ServerAuthorization) { - if (key == null) + if (masterKey == null) { - key = await GetMasterKeyAsync(); + masterKey = await GetMasterKeyAsync(); } - if (password == null || key == null) + if (password == null || masterKey == null) { throw new Exception("Invalid parameters."); } var iterations = hashPurpose == HashPurpose.LocalAuthorization ? 2 : 1; - var hash = await _cryptoFunctionService.Pbkdf2Async(key.Key, password, CryptoHashAlgorithm.Sha256, iterations); + var hash = await _cryptoFunctionService.Pbkdf2Async(masterKey.Key, password, CryptoHashAlgorithm.Sha256, iterations); return Convert.ToBase64String(hash); } - public Task SetPasswordHashAsync(string keyHash) + public Task SetMasterKeyHashAsync(string keyHash) { - _passwordHash = keyHash; + _masterKeyHash = keyHash; return _stateService.SetKeyHashAsync(keyHash); } - public async Task GetPasswordHashAsync() + public async Task GetMasterKeyHashAsync() { - if (_passwordHash != null) + if (_masterKeyHash != null) { - return _passwordHash; + return _masterKeyHash; } var passwordHash = await _stateService.GetKeyHashAsync(); if (passwordHash != null) { - _passwordHash = passwordHash; + _masterKeyHash = passwordHash; } - return _passwordHash; + return _masterKeyHash; } - public Task ClearPasswordHashAsync(string userId = null) + public Task ClearMasterKeyHashAsync(string userId = null) { - _passwordHash = null; + _masterKeyHash = null; return _stateService.SetKeyHashAsync(null, userId); } - // TODO(Jake): Uses Master Key - public async Task CompareAndUpdatePasswordHashAsync(string masterPassword, MasterKey key) + public async Task CompareAndUpdateKeyHashAsync(string masterPassword, MasterKey key) { - var storedPasswordHash = await GetPasswordHashAsync(); + var storedPasswordHash = await GetMasterKeyHashAsync(); if (masterPassword != null && storedPasswordHash != null) { - var localPasswordHash = await HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization); + var localPasswordHash = await HashMasterKeyAsync(masterPassword, key, HashPurpose.LocalAuthorization); if (localPasswordHash != null && storedPasswordHash == localPasswordHash) { return true; } - var serverPasswordHash = await HashPasswordAsync(masterPassword, key, HashPurpose.ServerAuthorization); + var serverPasswordHash = await HashMasterKeyAsync(masterPassword, key, HashPurpose.ServerAuthorization); if (serverPasswordHash != null & storedPasswordHash == serverPasswordHash) { - await SetPasswordHashAsync(localPasswordHash); + await SetMasterKeyHashAsync(localPasswordHash); return true; } } diff --git a/src/Core/Services/UserVerificationService.cs b/src/Core/Services/UserVerificationService.cs index 554c0d49a..74031b55c 100644 --- a/src/Core/Services/UserVerificationService.cs +++ b/src/Core/Services/UserVerificationService.cs @@ -44,7 +44,7 @@ namespace Bit.Core.Services } else { - var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(secret, null); + var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(secret, null); if (!passwordValid) { await InvalidSecretErrorAsync(verificationType); From 813ac841c6aad80d26b228b109af9ddaf2348783 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 20 Jul 2023 13:57:32 -0400 Subject: [PATCH 03/25] [PM-2713] fix casting issues and pin --- .../SettingsPage/SettingsPageViewModel.cs | 6 ++--- src/Core/Services/CryptoService.cs | 27 ++++++++++--------- .../BaseLockPasswordViewController.cs | 8 +++--- .../Controllers/LockPasswordViewController.cs | 8 +++--- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index f097b395c..1875405c8 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -439,18 +439,18 @@ namespace Bit.App.Pages var email = await _stateService.GetEmailAsync(); var pinKey = await _cryptoService.MakePinKeyAsync(pin, email, kdfConfig); var userKey = await _cryptoService.GetUserKeyAsync(); - var pinProtectedKey = await _cryptoService.EncryptAsync(userKey.Key, pinKey); + var protectedPinKey = await _cryptoService.EncryptAsync(userKey.Key, pinKey); var encPin = await _cryptoService.EncryptAsync(pin); await _stateService.SetProtectedPinAsync(encPin.EncryptedString); if (masterPassOnRestart) { - await _stateService.SetUserKeyPinEphemeralAsync(pinProtectedKey); + await _stateService.SetUserKeyPinEphemeralAsync(protectedPinKey); } else { - await _stateService.SetUserKeyPinAsync(pinProtectedKey); + await _stateService.SetUserKeyPinAsync(protectedPinKey); } } else diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index b336b3d4e..60e68c64d 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -146,7 +146,7 @@ namespace Bit.Core.Services public async Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null) { userKey ??= await GetUserKeyAsync(); - return await BuildProtectedSymmetricKey(masterKey, userKey.Key); + return await BuildProtectedSymmetricKey(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes)); } public async Task DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null) @@ -174,7 +174,7 @@ namespace Bit.Core.Services } else if (encUserKey.EncryptionType == EncryptionType.AesCbc256_HmacSha256_B64) { - var newKey = await StretchKeyAsync(masterKey); + var newKey = await StretchKeyAsync(masterKey, keyBytes => new MasterKey(keyBytes)); decUserKey = await DecryptToBytesAsync(encUserKey, newKey); } else @@ -197,7 +197,7 @@ namespace Bit.Core.Services } var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); - return await BuildProtectedSymmetricKey(key, newSymKey); + return await BuildProtectedSymmetricKey(key, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); } public async Task> MakeDataEncKeyAsync(OrgKey key) @@ -208,7 +208,7 @@ namespace Bit.Core.Services } var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); - return await BuildProtectedSymmetricKey(key, newSymKey); + return await BuildProtectedSymmetricKey(key, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); } public async Task HashMasterKeyAsync(string password, MasterKey masterKey, HashPurpose hashPurpose = HashPurpose.ServerAuthorization) @@ -423,7 +423,7 @@ namespace Bit.Core.Services public async Task MakePinKeyAsync(string pin, string salt, KdfConfig config) { var pinKey = await MakeKeyAsync(pin, salt, config, keyBytes => new PinKey(keyBytes)); - return await StretchKeyAsync(pinKey) as PinKey; + return await StretchKeyAsync(pinKey, keyBytes => new PinKey(keyBytes)); } public Task ClearPinKeysAsync(string userId = null) @@ -819,14 +819,16 @@ namespace Bit.Core.Services return key; } - private async Task StretchKeyAsync(SymmetricCryptoKey key) + // TODO: This needs to be moved into SymmetricCryptoKey model to remove the keyCreator hack + private async Task StretchKeyAsync(SymmetricCryptoKey key, Func keyCreator) + where TKey : SymmetricCryptoKey { var newKey = new byte[64]; var enc = await _cryptoFunctionService.HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("enc"), 32, HkdfAlgorithm.Sha256); Buffer.BlockCopy(enc, 0, newKey, 0, 32); var mac = await _cryptoFunctionService.HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("mac"), 32, HkdfAlgorithm.Sha256); Buffer.BlockCopy(mac, 0, newKey, 32, 32); - return new SymmetricCryptoKey(newKey); + return keyCreator(newKey); } private List HashPhrase(byte[] hash, int minimumEntropy = 64) @@ -853,13 +855,14 @@ namespace Bit.Core.Services return phrase; } - private async Task> BuildProtectedSymmetricKey(SymmetricCryptoKey key, - byte[] encKey) where T : SymmetricCryptoKey + // TODO: This needs to be moved into SymmetricCryptoKey model to remove the keyCreator hack + private async Task> BuildProtectedSymmetricKey(SymmetricCryptoKey key, + byte[] encKey, Func keyCreator) where TKey : SymmetricCryptoKey { EncString encKeyEnc = null; if (key.Key.Length == 32) { - var newKey = await StretchKeyAsync(key); + var newKey = await StretchKeyAsync(key, keyCreator); encKeyEnc = await EncryptAsync(encKey, newKey); } else if (key.Key.Length == 64) @@ -870,10 +873,10 @@ namespace Bit.Core.Services { throw new Exception("Invalid key size."); } - return new Tuple(new SymmetricCryptoKey(encKey) as T, encKeyEnc); + return new Tuple(keyCreator(encKey), encKeyEnc); } - // TODO: This intantiator needs to be moved into each key type in order to get rid of the keyCreator hack + // TODO: This needs to be moved into SymmetricCryptoKey model to remove the keyCreator hack private async Task MakeKeyAsync(string password, string salt, KdfConfig kdfConfig, Func keyCreator) where TKey : SymmetricCryptoKey { diff --git a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs index bebc7e925..165757d03 100644 --- a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs @@ -312,18 +312,18 @@ namespace Bit.iOS.Core.Controllers { var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig); - var storedPasswordHash = await _cryptoService.GetPasswordHashAsync(); + var storedPasswordHash = await _cryptoService.GetMasterKeyHashAsync(); if (storedPasswordHash == null) { var oldKey = await _secureStorageService.GetAsync("oldKey"); if (masterKey.KeyB64 == oldKey) { - var localPasswordHash = await _cryptoService.HashPasswordAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization); + var localPasswordHash = await _cryptoService.HashMasterKeyAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization); await _secureStorageService.RemoveAsync("oldKey"); - await _cryptoService.SetPasswordHashAsync(localPasswordHash); + await _cryptoService.SetMasterKeyHashAsync(localPasswordHash); } } - var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(inputtedValue, masterKey); + var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, masterKey); if (passwordValid) { await AppHelpers.ResetInvalidUnlockAttemptsAsync(); diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs index 9d4019a47..990e3d9f7 100644 --- a/src/iOS.Core/Controllers/LockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -285,18 +285,18 @@ namespace Bit.iOS.Core.Controllers { var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig); - var storedPasswordHash = await _cryptoService.GetPasswordHashAsync(); + var storedPasswordHash = await _cryptoService.GetMasterKeyHashAsync(); if (storedPasswordHash == null) { var oldKey = await _secureStorageService.GetAsync("oldKey"); if (masterKey.KeyB64 == oldKey) { - var localPasswordHash = await _cryptoService.HashPasswordAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization); + var localPasswordHash = await _cryptoService.HashMasterKeyAsync(inputtedValue, masterKey, HashPurpose.LocalAuthorization); await _secureStorageService.RemoveAsync("oldKey"); - await _cryptoService.SetPasswordHashAsync(localPasswordHash); + await _cryptoService.SetMasterKeyHashAsync(localPasswordHash); } } - var passwordValid = await _cryptoService.CompareAndUpdatePasswordHashAsync(inputtedValue, masterKey); + var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, masterKey); if (passwordValid) { await AppHelpers.ResetInvalidUnlockAttemptsAsync(); From a2f1ca583a1f2e4c716244e32d4a668ecf994c38 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 20 Jul 2023 16:14:40 -0400 Subject: [PATCH 04/25] [PM-2713] remove extra comment --- src/Core/Services/CryptoService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 60e68c64d..04e3b9bfd 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -683,7 +683,6 @@ namespace Bit.Core.Services ); 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); From 10574a7117b67356485c37a752d8516a194a005a Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 20 Jul 2023 16:17:30 -0400 Subject: [PATCH 05/25] [PM-2713] remove broken casting --- src/Core/Services/CryptoService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 04e3b9bfd..3ba541210 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -85,7 +85,7 @@ namespace Bit.Core.Services // Legacy support: encryption used to be done with the master key (derived from master password). // Users who have not migrated will have a null user key and must use the master key instead. - return (SymmetricCryptoKey)await GetMasterKeyAsync() as UserKey; + return new UserKey((await GetMasterKeyAsync()).Key); } public async Task HasUserKeyAsync(string userId = null) @@ -124,7 +124,7 @@ namespace Bit.Core.Services if (masterKey == null) { // Migration support - masterKey = await _stateService.GetKeyDecryptedAsync(userId) as MasterKey; + masterKey = new MasterKey((await _stateService.GetKeyDecryptedAsync(userId)).Key); if (masterKey != null) { await SetMasterKeyAsync(masterKey, userId); From 7562c688c59ff851e0a1e1700dbc99c99fafeeb0 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 27 Jul 2023 16:20:33 -0400 Subject: [PATCH 06/25] [PM-2713] deconstruct new key pair --- src/App/Pages/Accounts/SetPasswordPageViewModel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs index 6a358cc99..3164e3ccd 100644 --- a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs +++ b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs @@ -172,7 +172,7 @@ namespace Bit.App.Pages var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey, await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync()); - var keys = await _cryptoService.MakeKeyPairAsync(newUserKey); + var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey); var request = new SetPasswordRequest { MasterPasswordHash = masterPasswordHash, @@ -185,8 +185,8 @@ namespace Bit.App.Pages OrgIdentifier = OrgIdentifier, Keys = new KeysRequest { - PublicKey = keys.Item1, - EncryptedPrivateKey = keys.Item2.EncryptedString + PublicKey = newPublicKey, + EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString } }; @@ -199,7 +199,7 @@ namespace Bit.App.Pages await _cryptoService.SetMasterKeyAsync(newMasterKey); await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash); await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString); - await _cryptoService.SetPrivateKeyAsync(keys.Item2.EncryptedString); + await _cryptoService.SetPrivateKeyAsync(newProtectedPrivateKey.EncryptedString); if (ResetPasswordAutoEnroll) { From ba6d260565f7297551e536f9b1e83d2980b91aa9 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 27 Jul 2023 16:24:31 -0400 Subject: [PATCH 07/25] [PM-2713] rename PrivateKey methods to UserPrivateKey on crypto service --- src/App/Pages/Accounts/SetPasswordPageViewModel.cs | 2 +- src/Core/Abstractions/ICryptoService.cs | 4 ++-- src/Core/Services/AuthService.cs | 2 +- src/Core/Services/CryptoService.cs | 8 ++++---- src/Core/Services/SyncService.cs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs index 3164e3ccd..dd121c0a2 100644 --- a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs +++ b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs @@ -199,7 +199,7 @@ namespace Bit.App.Pages await _cryptoService.SetMasterKeyAsync(newMasterKey); await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash); await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString); - await _cryptoService.SetPrivateKeyAsync(newProtectedPrivateKey.EncryptedString); + await _cryptoService.SetUserPrivateKeyAsync(newProtectedPrivateKey.EncryptedString); if (ResetPasswordAutoEnroll) { diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index b91b22d26..777ff65f4 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -37,8 +37,8 @@ namespace Bit.Core.Abstractions Task> GetOrgKeysAsync(); Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null); Task GetPublicKeyAsync(); - Task SetPrivateKeyAsync(string encPrivateKey); - Task GetPrivateKeyAsync(); + Task SetUserPrivateKeyAsync(string encPrivateKey); + Task GetUserPrivateKeyAsync(); Task> GetFingerprintAsync(string userId, byte[] publicKey = null); Task> MakeKeyPairAsync(SymmetricCryptoKey key = null); Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null); diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index d12e8f914..8b15475aa 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -508,7 +508,7 @@ namespace Bit.Core.Services catch { } } - await _cryptoService.SetPrivateKeyAsync(tokenResponse.PrivateKey); + await _cryptoService.SetUserPrivateKeyAsync(tokenResponse.PrivateKey); } else if (tokenResponse.KeyConnectorUrl != null) { diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 3ba541210..a49bd808a 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -354,7 +354,7 @@ namespace Bit.Core.Services { return _publicKey; } - var privateKey = await GetPrivateKeyAsync(); + var privateKey = await GetUserPrivateKeyAsync(); if (privateKey == null) { return null; @@ -363,7 +363,7 @@ namespace Bit.Core.Services return _publicKey; } - public async Task SetPrivateKeyAsync(string encPrivateKey) + public async Task SetUserPrivateKeyAsync(string encPrivateKey) { if (encPrivateKey == null) { @@ -373,7 +373,7 @@ namespace Bit.Core.Services _privateKey = null; } - public async Task GetPrivateKeyAsync() + public async Task GetUserPrivateKeyAsync() { if (_privateKey != null) { @@ -519,7 +519,7 @@ namespace Bit.Core.Services if (privateKey is null) { - privateKey = await GetPrivateKeyAsync(); + privateKey = await GetUserPrivateKeyAsync(); } if (privateKey == null) diff --git a/src/Core/Services/SyncService.cs b/src/Core/Services/SyncService.cs index 2763a8e0d..e4dfd6660 100644 --- a/src/Core/Services/SyncService.cs +++ b/src/Core/Services/SyncService.cs @@ -328,7 +328,7 @@ namespace Bit.Core.Services return; } await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(response.Key); - await _cryptoService.SetPrivateKeyAsync(response.PrivateKey); + await _cryptoService.SetUserPrivateKeyAsync(response.PrivateKey); await _cryptoService.SetOrgKeysAsync(response.Organizations); await _stateService.SetSecurityStampAsync(response.SecurityStamp); var organizations = response.Organizations.ToDictionary(o => o.Id, o => new OrganizationData(o)); From de5113ede7b3fee049153eeb9e5ed4c89b18eff7 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 27 Jul 2023 16:26:23 -0400 Subject: [PATCH 08/25] [PM-2713] rename PinLockEnum to PinLockType --- src/App/Pages/Accounts/LockPageViewModel.cs | 12 ++++++------ .../SettingsPage/SettingsPageViewModel.cs | 2 +- src/Core/Abstractions/IVaultTimeoutService.cs | 2 +- src/Core/Services/VaultTimeoutService.cs | 16 +++++++--------- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 0a4d58c29..d8e620af0 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -38,7 +38,7 @@ namespace Bit.App.Pages private string _masterPassword; private string _pin; private bool _showPassword; - private PinLockEnum _pinStatus; + private PinLockType _pinStatus; private bool _pinEnabled; private bool _biometricEnabled; private bool _biometricIntegrityValid = true; @@ -165,8 +165,8 @@ namespace Bit.App.Pages var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); - PinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) || - _pinStatus == PinLockEnum.Persistent; + PinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) || + _pinStatus == PinLockType.Persistent; BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasEncryptedUserKeyAsync(); @@ -257,13 +257,13 @@ namespace Bit.App.Pages { EncString userKeyPin = null; EncString oldPinProtected = null; - if (_pinStatus == PinLockEnum.Persistent) + if (_pinStatus == PinLockType.Persistent) { userKeyPin = await _stateService.GetUserKeyPinAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; } - else if (_pinStatus == PinLockEnum.Transient) + else if (_pinStatus == PinLockType.Transient) { userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); @@ -273,7 +273,7 @@ namespace Bit.App.Pages if (oldPinProtected != null) { userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync( - _pinStatus == PinLockEnum.Transient, + _pinStatus == PinLockType.Transient, Pin, _email, kdfConfig, diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index 1875405c8..d2f80eca1 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -139,7 +139,7 @@ namespace Bit.App.Pages } var pinSet = await _vaultTimeoutService.IsPinLockSetAsync(); - _pin = pinSet != PinLockEnum.Disabled; + _pin = pinSet != PinLockType.Disabled; _biometric = await _vaultTimeoutService.IsBiometricLockSetAsync(); _screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync(); diff --git a/src/Core/Abstractions/IVaultTimeoutService.cs b/src/Core/Abstractions/IVaultTimeoutService.cs index 529806983..daf4865d2 100644 --- a/src/Core/Abstractions/IVaultTimeoutService.cs +++ b/src/Core/Abstractions/IVaultTimeoutService.cs @@ -17,7 +17,7 @@ namespace Bit.Core.Abstractions Task ShouldLockAsync(string userId = null); Task IsLoggedOutByTimeoutAsync(string userId = null); Task ShouldLogOutByTimeoutAsync(string userId = null); - Task IsPinLockSetAsync(string userId = null); + Task IsPinLockSetAsync(string userId = null); Task IsBiometricLockSetAsync(string userId = null); Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null); Task LogOutAsync(bool userInitiated = true, string userId = null); diff --git a/src/Core/Services/VaultTimeoutService.cs b/src/Core/Services/VaultTimeoutService.cs index f1060c2e1..920cba716 100644 --- a/src/Core/Services/VaultTimeoutService.cs +++ b/src/Core/Services/VaultTimeoutService.cs @@ -1,13 +1,11 @@ using System; -using System.Linq; using System.Threading.Tasks; using Bit.Core.Abstractions; using Bit.Core.Enums; -using Bit.Core.Models.Domain; namespace Bit.Core.Services { - public enum PinLockEnum + public enum PinLockType { Disabled, Persistent, @@ -175,8 +173,8 @@ namespace Bit.Core.Services var pinStatus = await IsPinLockSetAsync(userId); var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); - var pinEnabled = (pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) || - pinStatus == PinLockEnum.Persistent; + var pinEnabled = (pinStatus == PinLockType.Transient && ephemeralPinSet != null) || + pinStatus == PinLockType.Persistent; if (!pinEnabled && !await IsBiometricLockSetAsync()) { @@ -227,7 +225,7 @@ namespace Bit.Core.Services await _tokenService.ToggleTokensAsync(); } - public async Task IsPinLockSetAsync(string userId = null) + public async Task IsPinLockSetAsync(string userId = null) { // we can't depend on only the protected pin being set because old // versions only used it for MP on Restart @@ -237,15 +235,15 @@ namespace Bit.Core.Services if (userKeyPin != null || oldUserKeyPin != null) { - return PinLockEnum.Persistent; + return PinLockType.Persistent; } else if (pinIsEnabled != null && userKeyPin == null && oldUserKeyPin == null) { - return PinLockEnum.Transient; + return PinLockType.Transient; } else { - return PinLockEnum.Disabled; + return PinLockType.Disabled; } } From bb5a7383a81833a8d7e2d7e30a8d260b4d21339b Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Mon, 31 Jul 2023 12:42:56 -0400 Subject: [PATCH 09/25] [PM-2713] don't pass user key as param when encrypting --- src/App/Pages/Accounts/RegisterPageViewModel.cs | 5 +---- src/App/Pages/Accounts/SetPasswordPageViewModel.cs | 3 +-- src/Core/Abstractions/ICryptoService.cs | 2 +- src/Core/Services/AuthService.cs | 10 ++++++---- src/Core/Services/CryptoService.cs | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/App/Pages/Accounts/RegisterPageViewModel.cs b/src/App/Pages/Accounts/RegisterPageViewModel.cs index 52cca400c..f69453bad 100644 --- a/src/App/Pages/Accounts/RegisterPageViewModel.cs +++ b/src/App/Pages/Accounts/RegisterPageViewModel.cs @@ -178,10 +178,7 @@ namespace Bit.App.Pages Email = Email.Trim().ToLower(); var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null); var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig); - var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync( - newMasterKey, - await _cryptoService.MakeUserKeyAsync() - ); + var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey); var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey); var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey); var request = new RegisterRequest diff --git a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs index dd121c0a2..a933988a9 100644 --- a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs +++ b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs @@ -169,8 +169,7 @@ namespace Bit.App.Pages var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization); var localMasterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization); - var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey, - await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync()); + var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey); var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey); var request = new SetPasswordRequest diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 777ff65f4..106c43426 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -23,7 +23,7 @@ namespace Bit.Core.Abstractions Task GetMasterKeyAsync(string userId = null); Task MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig); Task ClearMasterKeyAsync(string userId = null); - Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null); + Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey); Task DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null); Task> MakeDataEncKeyAsync(UserKey key); Task> MakeDataEncKeyAsync(OrgKey key); diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index 8b15475aa..e6a8e9517 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -514,13 +514,15 @@ namespace Bit.Core.Services { // SSO Key Connector Onboarding var password = await _cryptoFunctionService.RandomBytesAsync(64); - var newMasterKey = await _cryptoService.MakeMasterKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig); + var newMasterKey = await _cryptoService.MakeMasterKeyAsync( + Convert.ToBase64String(password), + _tokenService.GetEmail(), + tokenResponse.KdfConfig); + var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64); await _cryptoService.SetMasterKeyAsync(newMasterKey); - var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync( - newMasterKey, - await _cryptoService.MakeUserKeyAsync()); + var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey); await _cryptoService.SetUserKeyAsync(newUserKey); var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(); diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index a49bd808a..6993eca2c 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -143,9 +143,9 @@ namespace Bit.Core.Services return _stateService.SetMasterKeyAsync(null, userId); } - public async Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null) + public async Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey) { - userKey ??= await GetUserKeyAsync(); + var userKey = await GetUserKeyAsync() ?? await MakeUserKeyAsync(); return await BuildProtectedSymmetricKey(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes)); } From c2ddbb7eff64791b707c6c002028d4e7059a2035 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Mon, 31 Jul 2023 12:57:50 -0400 Subject: [PATCH 10/25] [PM-2713] rename toggle method, don't reset enc user key --- .../Pages/Settings/SettingsPage/SettingsPageViewModel.cs | 2 +- src/Core/Abstractions/ICryptoService.cs | 2 +- src/Core/Services/CryptoService.cs | 7 +------ src/Core/Services/VaultTimeoutService.cs | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index d2f80eca1..4fa793753 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -491,7 +491,7 @@ namespace Bit.App.Pages await _stateService.SetBiometricUnlockAsync(null); } await _stateService.SetBiometricLockedAsync(false); - await _cryptoService.ToggleKeysAsync(); + await _cryptoService.RefreshKeysAsync(); BuildList(); } diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 106c43426..b170b3e23 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -10,7 +10,7 @@ namespace Bit.Core.Abstractions public interface ICryptoService { void ClearCache(); - Task ToggleKeysAsync(); + Task RefreshKeysAsync(); Task SetUserKeyAsync(UserKey userKey, string userId = null); Task GetUserKeyAsync(string userId = null); Task GetUserKeyWithLegacySupportAsync(string userId = null); diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 6993eca2c..0be99c7b6 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -43,15 +43,10 @@ namespace Bit.Core.Services _orgKeys = null; } - public async Task ToggleKeysAsync() + public async Task RefreshKeysAsync() { // refresh or clear the pin key await SetUserKeyAsync(await GetUserKeyAsync()); - - // refresh or clear the encrypted user key - var encUserKey = await _stateService.GetUserKeyMasterKeyAsync(); - await _stateService.SetUserKeyMasterKeyAsync(null); - await _stateService.SetUserKeyMasterKeyAsync(encUserKey); } public async Task SetUserKeyAsync(UserKey userKey, string userId = null) diff --git a/src/Core/Services/VaultTimeoutService.cs b/src/Core/Services/VaultTimeoutService.cs index 920cba716..55950f85b 100644 --- a/src/Core/Services/VaultTimeoutService.cs +++ b/src/Core/Services/VaultTimeoutService.cs @@ -221,7 +221,7 @@ namespace Bit.Core.Services { await _stateService.SetVaultTimeoutAsync(timeout); await _stateService.SetVaultTimeoutActionAsync(action); - await _cryptoService.ToggleKeysAsync(); + await _cryptoService.RefreshKeysAsync(); await _tokenService.ToggleTokensAsync(); } From c7932606892b1338844cc18bce07cfbd37495bd0 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Mon, 31 Jul 2023 13:13:29 -0400 Subject: [PATCH 11/25] [PM-2713] pr feedback --- src/Core/Services/CipherService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs index 89003249d..d04479045 100644 --- a/src/Core/Services/CipherService.cs +++ b/src/Core/Services/CipherService.cs @@ -249,8 +249,7 @@ namespace Bit.Core.Services { try { - var hashKey = await _cryptoService.HasUserKeyAsync(); - if (!hashKey) + if (!await _cryptoService.HasUserKeyAsync()) { throw new Exception("No key."); } From e323e196c05ae31e51b921e025ef8b488e3382ef Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Mon, 31 Jul 2023 15:31:46 -0400 Subject: [PATCH 12/25] [PM-2713] PR feedback --- src/Core/Abstractions/ICryptoService.cs | 2 +- src/Core/Services/CryptoService.cs | 63 ++++++++++--------- src/Core/Services/VaultTimeoutService.cs | 15 ++--- .../BaseLockPasswordViewController.cs | 14 ++--- .../Controllers/LockPasswordViewController.cs | 46 +++++++------- 5 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index b170b3e23..3970a8588 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -36,7 +36,7 @@ namespace Bit.Core.Abstractions Task GetOrgKeyAsync(string orgId); Task> GetOrgKeysAsync(); Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null); - Task GetPublicKeyAsync(); + Task GetUserPublicKeyAsync(); Task SetUserPrivateKeyAsync(string encPrivateKey); Task GetUserPrivateKeyAsync(); Task> GetFingerprintAsync(string userId, byte[] publicKey = null); diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 0be99c7b6..20df21640 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -56,7 +56,7 @@ namespace Bit.Core.Services // Refresh the Pin Key if the user has a Pin set if (await _stateService.GetProtectedPinAsync(userId) != null) { - await StorePinKey(userKey, userId); + await UpdateUserKeyPinAsync(userKey, userId); } else { @@ -174,7 +174,7 @@ namespace Bit.Core.Services } else { - throw new Exception("Unsupported encKey type."); + throw new Exception($"Unsupported encrypted user key type: {encUserKey.EncryptionType}"); } if (decUserKey == null) @@ -184,40 +184,45 @@ namespace Bit.Core.Services return new UserKey(decUserKey); } - public async Task> MakeDataEncKeyAsync(UserKey key) + public async Task> MakeDataEncKeyAsync(UserKey userKey) { - if (key == null) + if (userKey is null) { - throw new Exception("No user key provided"); + throw new ArgumentNullException(nameof(userKey)); } var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); - return await BuildProtectedSymmetricKey(key, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); + return await BuildProtectedSymmetricKey(userKey, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); } - public async Task> MakeDataEncKeyAsync(OrgKey key) + public async Task> MakeDataEncKeyAsync(OrgKey orgKey) { - if (key == null) + if (orgKey is null) { - throw new Exception("No org key provided"); + throw new ArgumentNullException(nameof(orgKey)); } var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); - return await BuildProtectedSymmetricKey(key, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); + return await BuildProtectedSymmetricKey(orgKey, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); } public async Task HashMasterKeyAsync(string password, MasterKey masterKey, HashPurpose hashPurpose = HashPurpose.ServerAuthorization) { - if (masterKey == null) + if (password is null) + { + throw new ArgumentNullException(nameof(password)); + } + + if (masterKey is null) { masterKey = await GetMasterKeyAsync(); + + if (masterKey is null) + { + throw new ArgumentNullException(nameof(masterKey)); + } } - if (password == null || masterKey == null) - { - throw new Exception("Invalid parameters."); - } - var iterations = hashPurpose == HashPurpose.LocalAuthorization ? 2 : 1; - var hash = await _cryptoFunctionService.Pbkdf2Async(masterKey.Key, password, CryptoHashAlgorithm.Sha256, iterations); + var hash = await _cryptoFunctionService.Pbkdf2Async(masterKey.Key, password, CryptoHashAlgorithm.Sha256, (int)hashPurpose); return Convert.ToBase64String(hash); } @@ -259,7 +264,7 @@ namespace Bit.Core.Services } var serverPasswordHash = await HashMasterKeyAsync(masterPassword, key, HashPurpose.ServerAuthorization); - if (serverPasswordHash != null & storedPasswordHash == serverPasswordHash) + if (serverPasswordHash != null && storedPasswordHash == serverPasswordHash) { await SetMasterKeyHashAsync(localPasswordHash); return true; @@ -283,11 +288,11 @@ namespace Bit.Core.Services return null; } var orgKeys = await GetOrgKeysAsync(); - if (orgKeys == null || !orgKeys.ContainsKey(orgId)) + if (orgKeys?.TryGetValue(orgId, out var orgKey) == true) { - return null; + return orgKey; } - return orgKeys[orgId]; + return null; } public Task> GetOrgKeysAsync() @@ -343,7 +348,7 @@ namespace Bit.Core.Services } } - public async Task GetPublicKeyAsync() + public async Task GetUserPublicKeyAsync() { if (_publicKey != null) { @@ -387,7 +392,7 @@ namespace Bit.Core.Services { if (publicKey == null) { - publicKey = await GetPublicKeyAsync(); + publicKey = await GetUserPublicKeyAsync(); } if (publicKey == null) { @@ -427,7 +432,7 @@ namespace Bit.Core.Services _stateService.SetUserKeyPinAsync(null, userId), _stateService.SetUserKeyPinEphemeralAsync(null, userId), _stateService.SetProtectedPinAsync(null, userId), - clearDeprecatedPinKeysAsync(userId)); + ClearDeprecatedPinKeysAsync(userId)); } public async Task DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null) @@ -439,8 +444,8 @@ namespace Bit.Core.Services throw new Exception("No PIN protected user key found."); } var pinKey = await MakePinKeyAsync(pin, salt, kdfConfig); - var userKey = await DecryptToBytesAsync(pinProtectedUserKey, pinKey); - return new UserKey(userKey); + var userKeyBytes = await DecryptToBytesAsync(pinProtectedUserKey, pinKey); + return new UserKey(userKeyBytes); } // Only for migration purposes @@ -474,7 +479,7 @@ namespace Bit.Core.Services { if (publicKey == null) { - publicKey = await GetPublicKeyAsync(); + publicKey = await GetUserPublicKeyAsync(); } if (publicKey == null) { @@ -668,7 +673,7 @@ namespace Bit.Core.Services // --HELPER METHODS-- - private async Task StorePinKey(UserKey userKey, string userId = null) + private async Task UpdateUserKeyPinAsync(UserKey userKey, string userId = null) { var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId))); var pinKey = await MakePinKeyAsync( @@ -974,7 +979,7 @@ namespace Bit.Core.Services return userKey; } - public Task clearDeprecatedPinKeysAsync(string userId = null) + public Task ClearDeprecatedPinKeysAsync(string userId = null) { return Task.WhenAll( _stateService.SetPinProtectedAsync(null, userId), diff --git a/src/Core/Services/VaultTimeoutService.cs b/src/Core/Services/VaultTimeoutService.cs index 55950f85b..1c1e61469 100644 --- a/src/Core/Services/VaultTimeoutService.cs +++ b/src/Core/Services/VaultTimeoutService.cs @@ -229,22 +229,19 @@ namespace Bit.Core.Services { // we can't depend on only the protected pin being set because old // versions only used it for MP on Restart - var pinIsEnabled = await _stateService.GetProtectedPinAsync(userId); - var userKeyPin = await _stateService.GetUserKeyPinAsync(userId); - var oldUserKeyPin = await _stateService.GetPinProtectedAsync(userId); + var isPinEnabled = await _stateService.GetProtectedPinAsync(userId) != null; + var hasUserKeyPin = await _stateService.GetUserKeyPinAsync(userId) != null; + var hasOldUserKeyPin = await _stateService.GetPinProtectedAsync(userId) != null; - if (userKeyPin != null || oldUserKeyPin != null) + if (hasUserKeyPin || hasOldUserKeyPin) { return PinLockType.Persistent; } - else if (pinIsEnabled != null && userKeyPin == null && oldUserKeyPin == null) + else if (isPinEnabled && !hasUserKeyPin && !hasOldUserKeyPin) { return PinLockType.Transient; } - else - { - return PinLockType.Disabled; - } + return PinLockType.Disabled; } public async Task IsBiometricLockSetAsync(string userId = null) diff --git a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs index 165757d03..c7e2a409d 100644 --- a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs @@ -30,7 +30,7 @@ namespace Bit.iOS.Core.Controllers private IBiometricService _biometricService; private IKeyConnectorService _keyConnectorService; private IAccountsManager _accountManager; - private PinLockEnum _pinStatus; + private PinLockType _pinStatus; private bool _pinEnabled; private bool _biometricEnabled; private bool _biometricIntegrityValid = true; @@ -104,7 +104,7 @@ namespace Bit.iOS.Core.Controllers if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync()) { _passwordReprompt = true; - _pinStatus = PinLockEnum.Disabled; + _pinStatus = PinLockType.Disabled; _pinEnabled = false; _biometricEnabled = false; } @@ -114,8 +114,8 @@ namespace Bit.iOS.Core.Controllers var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); - _pinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) || - _pinStatus == PinLockEnum.Persistent; + _pinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) || + _pinStatus == PinLockType.Persistent; _biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasEncryptedUserKeyAsync(); @@ -257,13 +257,13 @@ namespace Bit.iOS.Core.Controllers { EncString userKeyPin = null; EncString oldPinProtected = null; - if (_pinStatus == PinLockEnum.Persistent) + if (_pinStatus == PinLockType.Persistent) { userKeyPin = await _stateService.GetUserKeyPinAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; } - else if (_pinStatus == PinLockEnum.Transient) + else if (_pinStatus == PinLockType.Transient) { userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); @@ -273,7 +273,7 @@ namespace Bit.iOS.Core.Controllers if (oldPinProtected != null) { userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync( - _pinStatus == PinLockEnum.Transient, + _pinStatus == PinLockType.Transient, inputtedValue, email, kdfConfig, diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs index 990e3d9f7..8776bcc3a 100644 --- a/src/iOS.Core/Controllers/LockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -1,20 +1,20 @@ using System; -using UIKit; -using Foundation; -using Bit.iOS.Core.Views; -using Bit.App.Resources; -using Bit.iOS.Core.Utilities; -using Bit.App.Abstractions; -using Bit.Core.Abstractions; -using Bit.Core.Utilities; using System.Threading.Tasks; -using Bit.App.Utilities; -using Bit.Core.Models.Domain; -using Bit.Core.Enums; -using Bit.App.Pages; +using Bit.App.Abstractions; using Bit.App.Models; -using Xamarin.Forms; +using Bit.App.Pages; +using Bit.App.Resources; +using Bit.App.Utilities; +using Bit.Core.Abstractions; +using Bit.Core.Enums; +using Bit.Core.Models.Domain; using Bit.Core.Services; +using Bit.Core.Utilities; +using Bit.iOS.Core.Utilities; +using Bit.iOS.Core.Views; +using Foundation; +using UIKit; +using Xamarin.Forms; namespace Bit.iOS.Core.Controllers { @@ -30,7 +30,7 @@ namespace Bit.iOS.Core.Controllers private IPlatformUtilsService _platformUtilsService; private IBiometricService _biometricService; private IKeyConnectorService _keyConnectorService; - private PinLockEnum _pinStatus; + private PinLockType _pinStatus; private bool _pinEnabled; private bool _biometricEnabled; private bool _biometricIntegrityValid = true; @@ -96,7 +96,7 @@ namespace Bit.iOS.Core.Controllers if (autofillExtension && await _stateService.GetPasswordRepromptAutofillAsync()) { _passwordReprompt = true; - _pinStatus = PinLockEnum.Disabled; + _pinStatus = PinLockType.Disabled; _pinEnabled = false; _biometricEnabled = false; } @@ -106,8 +106,8 @@ namespace Bit.iOS.Core.Controllers var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); - _pinEnabled = (_pinStatus == PinLockEnum.Transient && ephemeralPinSet != null) || - _pinStatus == PinLockEnum.Persistent; + _pinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) || + _pinStatus == PinLockType.Persistent; _biometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasEncryptedUserKeyAsync(); @@ -129,7 +129,7 @@ namespace Bit.iOS.Core.Controllers { BaseNavItem.Title = AppResources.VerifyMasterPassword; } - + BaseCancelButton.Title = AppResources.Cancel; if (_biometricUnlockOnly) @@ -224,13 +224,13 @@ namespace Bit.iOS.Core.Controllers { EncString userKeyPin = null; EncString oldPinProtected = null; - if (_pinStatus == PinLockEnum.Persistent) + if (_pinStatus == PinLockType.Persistent) { userKeyPin = await _stateService.GetUserKeyPinAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; } - else if (_pinStatus == PinLockEnum.Transient) + else if (_pinStatus == PinLockType.Transient) { userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); @@ -240,7 +240,7 @@ namespace Bit.iOS.Core.Controllers if (oldPinProtected != null) { userKey = await _cryptoService.DecryptAndMigrateOldPinKeyAsync( - _pinStatus == PinLockEnum.Transient, + _pinStatus == PinLockType.Transient, inputtedValue, email, kdfConfig, @@ -284,7 +284,7 @@ namespace Bit.iOS.Core.Controllers else { var masterKey = await _cryptoService.MakeMasterKeyAsync(inputtedValue, email, kdfConfig); - + var storedPasswordHash = await _cryptoService.GetMasterKeyHashAsync(); if (storedPasswordHash == null) { @@ -395,7 +395,7 @@ namespace Bit.iOS.Core.Controllers }); PresentViewController(alert, true, null); } - + private async Task LogOutAsync() { await AppHelpers.LogOutAsync(await _stateService.GetActiveUserIdAsync()); From 89a9185b20e7272264e36d026a965393d2abf8af Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Mon, 31 Jul 2023 16:49:41 -0400 Subject: [PATCH 13/25] [PM-2713] rename get pin lock type method --- src/App/Pages/Accounts/LockPageViewModel.cs | 2 +- src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs | 2 +- src/Core/Abstractions/IVaultTimeoutService.cs | 2 +- src/Core/Services/VaultTimeoutService.cs | 4 ++-- src/iOS.Core/Controllers/BaseLockPasswordViewController.cs | 2 +- src/iOS.Core/Controllers/LockPasswordViewController.cs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index d8e620af0..775ab4cc0 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -161,7 +161,7 @@ namespace Bit.App.Pages public async Task InitAsync() { - _pinStatus = await _vaultTimeoutService.IsPinLockSetAsync(); + _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync(); var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index 4fa793753..10339992d 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -138,7 +138,7 @@ namespace Bit.App.Pages t.Value != null).ToList(); } - var pinSet = await _vaultTimeoutService.IsPinLockSetAsync(); + var pinSet = await _vaultTimeoutService.GetPinLockTypeAsync(); _pin = pinSet != PinLockType.Disabled; _biometric = await _vaultTimeoutService.IsBiometricLockSetAsync(); _screenCaptureAllowed = await _stateService.GetScreenCaptureAllowedAsync(); diff --git a/src/Core/Abstractions/IVaultTimeoutService.cs b/src/Core/Abstractions/IVaultTimeoutService.cs index daf4865d2..2c1f9598d 100644 --- a/src/Core/Abstractions/IVaultTimeoutService.cs +++ b/src/Core/Abstractions/IVaultTimeoutService.cs @@ -17,7 +17,7 @@ namespace Bit.Core.Abstractions Task ShouldLockAsync(string userId = null); Task IsLoggedOutByTimeoutAsync(string userId = null); Task ShouldLogOutByTimeoutAsync(string userId = null); - Task IsPinLockSetAsync(string userId = null); + Task GetPinLockTypeAsync(string userId = null); Task IsBiometricLockSetAsync(string userId = null); Task LockAsync(bool allowSoftLock = false, bool userInitiated = false, string userId = null); Task LogOutAsync(bool userInitiated = true, string userId = null); diff --git a/src/Core/Services/VaultTimeoutService.cs b/src/Core/Services/VaultTimeoutService.cs index 1c1e61469..28a46f7f7 100644 --- a/src/Core/Services/VaultTimeoutService.cs +++ b/src/Core/Services/VaultTimeoutService.cs @@ -170,7 +170,7 @@ namespace Bit.Core.Services if (await _keyConnectorService.GetUsesKeyConnector()) { - var pinStatus = await IsPinLockSetAsync(userId); + var pinStatus = await GetPinLockTypeAsync(userId); var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); var pinEnabled = (pinStatus == PinLockType.Transient && ephemeralPinSet != null) || @@ -225,7 +225,7 @@ namespace Bit.Core.Services await _tokenService.ToggleTokensAsync(); } - public async Task IsPinLockSetAsync(string userId = null) + public async Task GetPinLockTypeAsync(string userId = null) { // we can't depend on only the protected pin being set because old // versions only used it for MP on Restart diff --git a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs index c7e2a409d..c5f2dfabe 100644 --- a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs @@ -110,7 +110,7 @@ namespace Bit.iOS.Core.Controllers } else { - _pinStatus = await _vaultTimeoutService.IsPinLockSetAsync(); + _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync(); var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs index 8776bcc3a..981bd5d88 100644 --- a/src/iOS.Core/Controllers/LockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -102,7 +102,7 @@ namespace Bit.iOS.Core.Controllers } else { - _pinStatus = await _vaultTimeoutService.IsPinLockSetAsync(); + _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync(); var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); From 3e87d74061c4778504625ee51681830bf2d5bb62 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Mon, 31 Jul 2023 17:09:47 -0400 Subject: [PATCH 14/25] [PM-2713] revert feedback for build --- src/Core/Services/CryptoService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 20df21640..d0d2ab52b 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -288,11 +288,11 @@ namespace Bit.Core.Services return null; } var orgKeys = await GetOrgKeysAsync(); - if (orgKeys?.TryGetValue(orgId, out var orgKey) == true) + if (orgKeys == null || !orgKeys.ContainsKey(orgId)) { - return orgKey; + return null; } - return null; + return orgKeys[orgId]; } public Task> GetOrgKeysAsync() From 61aac20555ccf9f87ec17466b358adb5b187aa49 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Tue, 1 Aug 2023 08:46:02 -0400 Subject: [PATCH 15/25] [PM-2713] rename state methods --- src/App/Pages/Accounts/LockPageViewModel.cs | 6 ++-- .../SettingsPage/SettingsPageViewModel.cs | 4 +-- src/Core/Abstractions/IStateService.cs | 12 ++++---- src/Core/Services/CryptoService.cs | 28 +++++++++---------- src/Core/Services/StateService.cs | 20 ++++++------- src/Core/Services/VaultTimeoutService.cs | 4 +-- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 775ab4cc0..b242e7fdb 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -163,7 +163,7 @@ namespace Bit.App.Pages { _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync(); - var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() + var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); PinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) || _pinStatus == PinLockType.Persistent; @@ -259,13 +259,13 @@ namespace Bit.App.Pages EncString oldPinProtected = null; if (_pinStatus == PinLockType.Persistent) { - userKeyPin = await _stateService.GetUserKeyPinAsync(); + userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; } else if (_pinStatus == PinLockType.Transient) { - userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); + userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); } diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index 10339992d..46ce7a52e 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -446,11 +446,11 @@ namespace Bit.App.Pages if (masterPassOnRestart) { - await _stateService.SetUserKeyPinEphemeralAsync(protectedPinKey); + await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(protectedPinKey); } else { - await _stateService.SetUserKeyPinAsync(protectedPinKey); + await _stateService.SetPinKeyEncryptedUserKeyAsync(protectedPinKey); } } else diff --git a/src/Core/Abstractions/IStateService.cs b/src/Core/Abstractions/IStateService.cs index 714c7345b..2f9c11be5 100644 --- a/src/Core/Abstractions/IStateService.cs +++ b/src/Core/Abstractions/IStateService.cs @@ -17,8 +17,8 @@ namespace Bit.Core.Abstractions Task SetUserKeyAsync(UserKey value, string userId = null); Task GetMasterKeyAsync(string userId = null); Task SetMasterKeyAsync(MasterKey value, string userId = null); - Task GetUserKeyMasterKeyAsync(string userId = null); - Task SetUserKeyMasterKeyAsync(string value, string userId = null); + Task GetMasterKeyEncryptedUserKeyAsync(string userId = null); + Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null); Task GetActiveUserIdAsync(); Task GetActiveUserEmailAsync(); Task GetActiveUserCustomDataAsync(Func dataMapper); @@ -44,10 +44,10 @@ namespace Bit.Core.Abstractions Task CanAccessPremiumAsync(string userId = null); Task GetProtectedPinAsync(string userId = null); Task SetPersonalPremiumAsync(bool 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 GetPinKeyEncryptedUserKeyAsync(string userId = null); + Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null); + Task GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null); + Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null); Task SetProtectedPinAsync(string value, string userId = null); [Obsolete("Use GetUserKeyPinAsync instead, left for migration purposes")] Task GetPinProtectedAsync(string userId = null); diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index d0d2ab52b..78fd431b2 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -60,8 +60,8 @@ namespace Bit.Core.Services } else { - await _stateService.SetUserKeyPinAsync(null, userId); - await _stateService.SetUserKeyPinEphemeralAsync(null, userId); + await _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId); + await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId); } } @@ -90,7 +90,7 @@ namespace Bit.Core.Services public async Task HasEncryptedUserKeyAsync(string userId = null) { - return await _stateService.GetUserKeyMasterKeyAsync(userId) != null; + return await _stateService.GetMasterKeyEncryptedUserKeyAsync(userId) != null; } public async Task MakeUserKeyAsync() @@ -105,7 +105,7 @@ namespace Bit.Core.Services public Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null) { - return _stateService.SetUserKeyMasterKeyAsync(value, userId); + return _stateService.SetMasterKeyEncryptedUserKeyAsync(value, userId); } public Task SetMasterKeyAsync(MasterKey masterKey, string userId = null) @@ -154,7 +154,7 @@ namespace Bit.Core.Services if (encUserKey == null) { - var userKeyMasterKey = await _stateService.GetUserKeyMasterKeyAsync(userId); + var userKeyMasterKey = await _stateService.GetMasterKeyEncryptedUserKeyAsync(userId); if (userKeyMasterKey == null) { throw new Exception("No encrypted user key found"); @@ -429,16 +429,16 @@ namespace Bit.Core.Services public Task ClearPinKeysAsync(string userId = null) { return Task.WhenAll( - _stateService.SetUserKeyPinAsync(null, userId), - _stateService.SetUserKeyPinEphemeralAsync(null, userId), + _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId), + _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId), _stateService.SetProtectedPinAsync(null, userId), ClearDeprecatedPinKeysAsync(userId)); } public async Task DecryptUserKeyWithPinAsync(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null) { - pinProtectedUserKey ??= await _stateService.GetUserKeyPinAsync(); - pinProtectedUserKey ??= await _stateService.GetUserKeyPinEphemeralAsync(); + pinProtectedUserKey ??= await _stateService.GetPinKeyEncryptedUserKeyAsync(); + pinProtectedUserKey ??= await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync(); if (pinProtectedUserKey == null) { throw new Exception("No PIN protected user key found."); @@ -683,12 +683,12 @@ namespace Bit.Core.Services ); var encPin = await EncryptAsync(userKey.Key, pinKey); - if (await _stateService.GetUserKeyPinAsync(userId) != null) + if (await _stateService.GetPinKeyEncryptedUserKeyAsync(userId) != null) { - await _stateService.SetUserKeyPinAsync(encPin, userId); + await _stateService.SetPinKeyEncryptedUserKeyAsync(encPin, userId); return; } - await _stateService.SetUserKeyPinEphemeralAsync(encPin, userId); + await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(encPin, userId); } private async Task AesEncryptAsync(byte[] data, SymmetricCryptoKey key) @@ -964,12 +964,12 @@ namespace Bit.Core.Services if (masterPasswordOnRestart) { await _stateService.SetPinProtectedKeyAsync(null); - await _stateService.SetUserKeyPinEphemeralAsync(pinProtectedKey); + await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(pinProtectedKey); } else { await _stateService.SetPinProtectedAsync(null); - await _stateService.SetUserKeyPinAsync(pinProtectedKey); + await _stateService.SetPinKeyEncryptedUserKeyAsync(pinProtectedKey); // We previously only set the protected pin if MP on Restart was enabled // now we set it regardless diff --git a/src/Core/Services/StateService.cs b/src/Core/Services/StateService.cs index 163f29d2a..2caa36144 100644 --- a/src/Core/Services/StateService.cs +++ b/src/Core/Services/StateService.cs @@ -334,12 +334,12 @@ namespace Bit.Core.Services await SaveAccountAsync(account, reconciledOptions); } - public async Task GetUserKeyMasterKeyAsync(string userId = null) + public async Task GetMasterKeyEncryptedUserKeyAsync(string userId = null) { return await _storageMediatorService.GetAsync(Constants.UserKeyKey(userId), false); } - public async Task SetUserKeyMasterKeyAsync(string value, string userId = null) + public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null) { await _storageMediatorService.SaveAsync(Constants.UserKeyKey(userId), value, false); } @@ -395,25 +395,25 @@ namespace Bit.Core.Services await SetValueAsync(Constants.ProtectedPinKey(reconciledOptions.UserId), value, reconciledOptions); } - public async Task GetUserKeyPinAsync(string userId = null) + public async Task GetPinKeyEncryptedUserKeyAsync(string userId = null) { var key = await _storageMediatorService.GetAsync(Constants.UserKeyPinKey(userId), false); return key != null ? new EncString(key) : null; } - public async Task SetUserKeyPinAsync(EncString value, string userId = null) + public async Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null) { await _storageMediatorService.SaveAsync(Constants.UserKeyPinKey(userId), value?.EncryptedString, false); } - public async Task GetUserKeyPinEphemeralAsync(string userId = null) + public async Task GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null) { return (await GetAccountAsync( ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()) ))?.VolatileData?.UserKeyPinEphemeral; } - public async Task SetUserKeyPinEphemeralAsync(EncString value, string userId = null) + public async Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()); @@ -422,7 +422,7 @@ namespace Bit.Core.Services await SaveAccountAsync(account, reconciledOptions); } - [Obsolete("Use GetUserKeyPinAsync instead, left for migration purposes")] + [Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")] public async Task GetPinProtectedAsync(string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, @@ -430,7 +430,7 @@ namespace Bit.Core.Services return await GetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), reconciledOptions); } - [Obsolete("Use SetUserKeyPinAsync instead")] + [Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead")] public async Task SetPinProtectedAsync(string value, string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, @@ -438,7 +438,7 @@ namespace Bit.Core.Services await SetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), value, reconciledOptions); } - [Obsolete("Use GetUserKeyPinEphemeralAsync instead, left for migration purposes")] + [Obsolete("Use GetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")] public async Task GetPinProtectedKeyAsync(string userId = null) { return (await GetAccountAsync( @@ -446,7 +446,7 @@ namespace Bit.Core.Services ))?.VolatileData?.PinProtectedKey; } - [Obsolete("Use SetUserKeyPinEphemeralAsync instead")] + [Obsolete("Use SetPinKeyEncryptedUserKeyEphemeralAsync instead")] public async Task SetPinProtectedKeyAsync(EncString value, string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, diff --git a/src/Core/Services/VaultTimeoutService.cs b/src/Core/Services/VaultTimeoutService.cs index 28a46f7f7..28639aeea 100644 --- a/src/Core/Services/VaultTimeoutService.cs +++ b/src/Core/Services/VaultTimeoutService.cs @@ -171,7 +171,7 @@ namespace Bit.Core.Services if (await _keyConnectorService.GetUsesKeyConnector()) { var pinStatus = await GetPinLockTypeAsync(userId); - var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() + var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); var pinEnabled = (pinStatus == PinLockType.Transient && ephemeralPinSet != null) || pinStatus == PinLockType.Persistent; @@ -230,7 +230,7 @@ namespace Bit.Core.Services // we can't depend on only the protected pin being set because old // versions only used it for MP on Restart var isPinEnabled = await _stateService.GetProtectedPinAsync(userId) != null; - var hasUserKeyPin = await _stateService.GetUserKeyPinAsync(userId) != null; + var hasUserKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync(userId) != null; var hasOldUserKeyPin = await _stateService.GetPinProtectedAsync(userId) != null; if (hasUserKeyPin || hasOldUserKeyPin) From b1eb263fef39e401b3608202562fb60e80d4b921 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Tue, 1 Aug 2023 08:54:19 -0400 Subject: [PATCH 16/25] [PM-2713] combine makeDataEncKey methods --- src/Core/Abstractions/ICryptoService.cs | 3 +-- src/Core/Services/CryptoService.cs | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 3970a8588..564440bbe 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -25,8 +25,7 @@ namespace Bit.Core.Abstractions Task ClearMasterKeyAsync(string userId = null); Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey); Task DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null); - Task> MakeDataEncKeyAsync(UserKey key); - Task> MakeDataEncKeyAsync(OrgKey key); + Task> MakeDataEncKeyAsync(TKey key) where TKey : SymmetricCryptoKey; Task HashMasterKeyAsync(string password, MasterKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); Task SetMasterKeyHashAsync(string keyHash); Task GetMasterKeyHashAsync(); diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 78fd431b2..e381423d6 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -184,26 +184,20 @@ namespace Bit.Core.Services return new UserKey(decUserKey); } - public async Task> MakeDataEncKeyAsync(UserKey userKey) + public async Task> MakeDataEncKeyAsync(TKey key) + where TKey : SymmetricCryptoKey { - if (userKey is null) + if (key is null) { - throw new ArgumentNullException(nameof(userKey)); + throw new ArgumentNullException(nameof(key)); + } + if (typeof(TKey) != typeof(UserKey) && typeof(TKey) != typeof(OrgKey)) + { + throw new ArgumentException($"Data encryption keys must be of type UserKey or OrgKey. {typeof(TKey)} unsupported."); } var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); - return await BuildProtectedSymmetricKey(userKey, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); - } - - public async Task> MakeDataEncKeyAsync(OrgKey orgKey) - { - if (orgKey is null) - { - throw new ArgumentNullException(nameof(orgKey)); - } - - var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); - return await BuildProtectedSymmetricKey(orgKey, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); + return await BuildProtectedSymmetricKey(key, newSymKey, keyBytes => new SymmetricCryptoKey(keyBytes)); } public async Task HashMasterKeyAsync(string password, MasterKey masterKey, HashPurpose hashPurpose = HashPurpose.ServerAuthorization) From 7fb89fa1a575254b674c3626c05517439976e120 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Tue, 1 Aug 2023 09:30:00 -0400 Subject: [PATCH 17/25] [PM-2713] consolidate attachment key creation - also fix ios files missed during symbol rename --- src/Core/Services/CipherService.cs | 45 ++++++++----------- .../BaseLockPasswordViewController.cs | 6 +-- .../Controllers/LockPasswordViewController.cs | 6 +-- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs index d04479045..5b424f1b5 100644 --- a/src/Core/Services/CipherService.cs +++ b/src/Core/Services/CipherService.cs @@ -556,20 +556,9 @@ namespace Bit.Core.Services public async Task SaveAttachmentRawWithServerAsync(Cipher cipher, string filename, byte[] data) { - SymmetricCryptoKey attachmentKey; - EncString protectedAttachmentKey; - var orgKey = await _cryptoService.GetOrgKeyAsync(cipher.OrganizationId); - if (orgKey != null) - { - (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey); - } - else - { - var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync(); - (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey); - } + var (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(cipher.OrganizationId); - var encFileName = await _cryptoService.EncryptAsync(filename, orgKey); + var encFileName = await _cryptoService.EncryptAsync(filename, encKey); var encFileData = await _cryptoService.EncryptToBytesAsync(data, attachmentKey); CipherResponse response; @@ -806,6 +795,21 @@ namespace Bit.Core.Services // Helpers + private async Task> MakeAttachmentKeyAsync(string organizationId) + { + SymmetricCryptoKey attachmentKey; + EncString protectedAttachmentKey; + var orgKey = await _cryptoService.GetOrgKeyAsync(organizationId); + if (orgKey != null) + { + (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey); + return new Tuple(attachmentKey, protectedAttachmentKey, orgKey); + } + var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync(); + (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey); + return new Tuple(attachmentKey, protectedAttachmentKey, userKey); + } + private async Task ShareAttachmentWithServerAsync(AttachmentView attachmentView, string cipherId, string organizationId) { @@ -818,20 +822,9 @@ namespace Bit.Core.Services var bytes = await attachmentResponse.Content.ReadAsByteArrayAsync(); var decBytes = await _cryptoService.DecryptFromBytesAsync(bytes, null); - SymmetricCryptoKey attachmentKey; - EncString protectedAttachmentKey; - var orgKey = await _cryptoService.GetOrgKeyAsync(organizationId); - if (orgKey != null) - { - (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey); - } - else - { - var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync(); - (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey); - } + var (attachmentKey, protectedAttachmentKey, encKey) = await MakeAttachmentKeyAsync(organizationId); - var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, orgKey); + var encFileName = await _cryptoService.EncryptAsync(attachmentView.FileName, encKey); var encFileData = await _cryptoService.EncryptToBytesAsync(decBytes, attachmentKey); var boundary = string.Concat("--BWMobileFormBoundary", DateTime.UtcNow.Ticks); diff --git a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs index c5f2dfabe..db5ce6f67 100644 --- a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs @@ -112,7 +112,7 @@ namespace Bit.iOS.Core.Controllers { _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync(); - var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() + var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); _pinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) || _pinStatus == PinLockType.Persistent; @@ -259,13 +259,13 @@ namespace Bit.iOS.Core.Controllers EncString oldPinProtected = null; if (_pinStatus == PinLockType.Persistent) { - userKeyPin = await _stateService.GetUserKeyPinAsync(); + userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; } else if (_pinStatus == PinLockType.Transient) { - userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); + userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); } diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs index 981bd5d88..eff86b50e 100644 --- a/src/iOS.Core/Controllers/LockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -104,7 +104,7 @@ namespace Bit.iOS.Core.Controllers { _pinStatus = await _vaultTimeoutService.GetPinLockTypeAsync(); - var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync() + var ephemeralPinSet = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync() ?? await _stateService.GetPinProtectedKeyAsync(); _pinEnabled = (_pinStatus == PinLockType.Transient && ephemeralPinSet != null) || _pinStatus == PinLockType.Persistent; @@ -226,13 +226,13 @@ namespace Bit.iOS.Core.Controllers EncString oldPinProtected = null; if (_pinStatus == PinLockType.Persistent) { - userKeyPin = await _stateService.GetUserKeyPinAsync(); + userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyAsync(); var oldEncryptedKey = await _stateService.GetPinProtectedAsync(); oldPinProtected = oldEncryptedKey != null ? new EncString(oldEncryptedKey) : null; } else if (_pinStatus == PinLockType.Transient) { - userKeyPin = await _stateService.GetUserKeyPinEphemeralAsync(); + userKeyPin = await _stateService.GetPinKeyEncryptedUserKeyEphemeralAsync(); oldPinProtected = await _stateService.GetPinProtectedKeyAsync(); } From 1e8ed1b5ce8b9a67800d4130d7993eeafc7146e6 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Tue, 1 Aug 2023 10:47:02 -0400 Subject: [PATCH 18/25] [PM-2713] replace generic with inherited class --- src/Core/Abstractions/ICryptoService.cs | 2 +- src/Core/Abstractions/IStateService.cs | 28 ++++---- src/Core/Services/CipherService.cs | 15 ++-- src/Core/Services/CryptoService.cs | 9 +-- src/Core/Services/StateService.cs | 93 ++++++++++--------------- 5 files changed, 60 insertions(+), 87 deletions(-) diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 564440bbe..040068b24 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -25,7 +25,7 @@ namespace Bit.Core.Abstractions Task ClearMasterKeyAsync(string userId = null); Task> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey); Task DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null); - Task> MakeDataEncKeyAsync(TKey key) where TKey : SymmetricCryptoKey; + Task> MakeDataEncKeyAsync(SymmetricCryptoKey key); Task HashMasterKeyAsync(string password, MasterKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); Task SetMasterKeyHashAsync(string keyHash); Task GetMasterKeyHashAsync(); diff --git a/src/Core/Abstractions/IStateService.cs b/src/Core/Abstractions/IStateService.cs index 2f9c11be5..add556dcf 100644 --- a/src/Core/Abstractions/IStateService.cs +++ b/src/Core/Abstractions/IStateService.cs @@ -49,14 +49,6 @@ namespace Bit.Core.Abstractions Task GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null); Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null); Task SetProtectedPinAsync(string value, string userId = null); - [Obsolete("Use GetUserKeyPinAsync instead, left for migration purposes")] - Task GetPinProtectedAsync(string userId = null); - [Obsolete("Use SetUserKeyPinAsync instead")] - Task SetPinProtectedAsync(string value, string userId = null); - [Obsolete("Use GetUserKeyPinEphemeralAsync instead, left for migration purposes")] - Task GetPinProtectedKeyAsync(string userId = null); - [Obsolete("Use SetUserKeyPinEphemeralAsync instead")] - Task SetPinProtectedKeyAsync(EncString value, string userId = null); Task SetKdfConfigurationAsync(KdfConfig config, string userId = null); Task GetKeyHashAsync(string userId = null); Task SetKeyHashAsync(string value, string userId = null); @@ -184,17 +176,21 @@ namespace Bit.Core.Abstractions void SetLocale(string locale); ConfigResponse GetConfigs(); void SetConfigs(ConfigResponse value); - [Obsolete("Use GetUserKeyMasterKey instead")] + [Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")] + Task GetPinProtectedAsync(string userId = null); + [Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")] + Task SetPinProtectedAsync(string value, string userId = null); + [Obsolete("Use GetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")] + Task GetPinProtectedKeyAsync(string userId = null); + [Obsolete("Use SetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")] + Task SetPinProtectedKeyAsync(EncString value, string userId = null); + [Obsolete("Use GetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")] Task GetEncKeyEncryptedAsync(string userId = null); - [Obsolete("Use SetUserKeyMasterKey instead")] + [Obsolete("Use SetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")] Task SetEncKeyEncryptedAsync(string value, string userId = null); - [Obsolete] - Task GetKeyEncryptedAsync(string userId = null); - [Obsolete] + [Obsolete("Left for migration purposes")] Task SetKeyEncryptedAsync(string value, string userId = null); - [Obsolete("Use GetMasterKey instead")] + [Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")] Task GetKeyDecryptedAsync(string userId = null); - [Obsolete("Use GetMasterKey instead")] - Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null); } } diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs index 5b424f1b5..d34b20e43 100644 --- a/src/Core/Services/CipherService.cs +++ b/src/Core/Services/CipherService.cs @@ -797,17 +797,10 @@ namespace Bit.Core.Services private async Task> MakeAttachmentKeyAsync(string organizationId) { - SymmetricCryptoKey attachmentKey; - EncString protectedAttachmentKey; - var orgKey = await _cryptoService.GetOrgKeyAsync(organizationId); - if (orgKey != null) - { - (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(orgKey); - return new Tuple(attachmentKey, protectedAttachmentKey, orgKey); - } - var userKey = await _cryptoService.GetUserKeyWithLegacySupportAsync(); - (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(userKey); - return new Tuple(attachmentKey, protectedAttachmentKey, userKey); + var encryptionKey = await _cryptoService.GetOrgKeyAsync(organizationId) + ?? (SymmetricCryptoKey)await _cryptoService.GetUserKeyWithLegacySupportAsync(); + var (attachmentKey, protectedAttachmentKey) = await _cryptoService.MakeDataEncKeyAsync(encryptionKey); + return new Tuple(attachmentKey, protectedAttachmentKey, encryptionKey); } private async Task ShareAttachmentWithServerAsync(AttachmentView attachmentView, string cipherId, diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index e381423d6..5786cb19c 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -184,16 +184,15 @@ namespace Bit.Core.Services return new UserKey(decUserKey); } - public async Task> MakeDataEncKeyAsync(TKey key) - where TKey : SymmetricCryptoKey + public async Task> MakeDataEncKeyAsync(SymmetricCryptoKey key) { if (key is null) { throw new ArgumentNullException(nameof(key)); } - if (typeof(TKey) != typeof(UserKey) && typeof(TKey) != typeof(OrgKey)) + if (!(key is UserKey) && !(key is OrgKey)) { - throw new ArgumentException($"Data encryption keys must be of type UserKey or OrgKey. {typeof(TKey)} unsupported."); + throw new ArgumentException($"Data encryption keys must be of type UserKey or OrgKey. {key.GetType().FullName} unsupported."); } var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64); @@ -970,6 +969,8 @@ namespace Bit.Core.Services var encPin = await EncryptAsync(pin, userKey); await _stateService.SetProtectedPinAsync(encPin.EncryptedString); } + // Clear old key + await _stateService.SetEncKeyEncryptedAsync(null); return userKey; } diff --git a/src/Core/Services/StateService.cs b/src/Core/Services/StateService.cs index 2caa36144..27349421c 100644 --- a/src/Core/Services/StateService.cs +++ b/src/Core/Services/StateService.cs @@ -422,39 +422,6 @@ namespace Bit.Core.Services await SaveAccountAsync(account, reconciledOptions); } - [Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")] - public async Task GetPinProtectedAsync(string userId = null) - { - var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, - await GetDefaultStorageOptionsAsync()); - return await GetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), reconciledOptions); - } - - [Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead")] - public async Task SetPinProtectedAsync(string value, string userId = null) - { - var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, - await GetDefaultStorageOptionsAsync()); - await SetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), value, reconciledOptions); - } - - [Obsolete("Use GetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")] - public async Task GetPinProtectedKeyAsync(string userId = null) - { - return (await GetAccountAsync( - ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()) - ))?.VolatileData?.PinProtectedKey; - } - - [Obsolete("Use SetPinKeyEncryptedUserKeyEphemeralAsync instead")] - public async Task SetPinProtectedKeyAsync(EncString value, string userId = null) - { - var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, - await GetDefaultInMemoryOptionsAsync()); - var account = await GetAccountAsync(reconciledOptions); - account.VolatileData.PinProtectedKey = value; - await SaveAccountAsync(account, reconciledOptions); - } public async Task SetKdfConfigurationAsync(KdfConfig config, string userId = null) { @@ -1688,7 +1655,41 @@ namespace Bit.Core.Services shouldConnect ?? await GetShouldConnectToWatchAsync(), await GetDefaultStorageOptionsAsync()); } - [Obsolete] + [Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")] + public async Task GetPinProtectedAsync(string userId = null) + { + var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, + await GetDefaultStorageOptionsAsync()); + return await GetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), reconciledOptions); + } + + [Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead")] + public async Task SetPinProtectedAsync(string value, string userId = null) + { + var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, + await GetDefaultStorageOptionsAsync()); + await SetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), value, reconciledOptions); + } + + [Obsolete("Use GetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")] + public async Task GetPinProtectedKeyAsync(string userId = null) + { + return (await GetAccountAsync( + ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()) + ))?.VolatileData?.PinProtectedKey; + } + + [Obsolete("Use SetPinKeyEncryptedUserKeyEphemeralAsync instead")] + public async Task SetPinProtectedKeyAsync(EncString value, string userId = null) + { + var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, + await GetDefaultInMemoryOptionsAsync()); + var account = await GetAccountAsync(reconciledOptions); + account.VolatileData.PinProtectedKey = value; + await SaveAccountAsync(account, reconciledOptions); + } + + [Obsolete("Use GetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")] public async Task GetEncKeyEncryptedAsync(string userId = null) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, @@ -1696,7 +1697,7 @@ namespace Bit.Core.Services return await GetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), reconciledOptions); } - [Obsolete] + [Obsolete("Use SetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")] public async Task SetEncKeyEncryptedAsync(string value, string userId) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, @@ -1704,15 +1705,7 @@ namespace Bit.Core.Services await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions); } - [Obsolete] - public async Task GetKeyEncryptedAsync(string userId = null) - { - var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, - await GetDefaultSecureStorageOptionsAsync()); - return await GetValueAsync(Constants.KeyKey(reconciledOptions.UserId), reconciledOptions); - } - - [Obsolete] + [Obsolete("Left for migration purposes")] public async Task SetKeyEncryptedAsync(string value, string userId) { var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, @@ -1720,22 +1713,12 @@ namespace Bit.Core.Services await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions); } - [Obsolete] + [Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")] public async Task GetKeyDecryptedAsync(string userId = null) { return (await GetAccountAsync( ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync()) ))?.VolatileData?.Key; } - - [Obsolete] - public async Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null) - { - var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, - await GetDefaultInMemoryOptionsAsync()); - var account = await GetAccountAsync(reconciledOptions); - account.VolatileData.Key = value; - await SaveAccountAsync(account, reconciledOptions); - } } } From ee0dcd23f5844b2c9e03985a65bca5abce4d6c08 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Tue, 1 Aug 2023 20:08:41 -0400 Subject: [PATCH 19/25] rename account keys to be more descriptive --- src/Core/Constants.cs | 4 ++-- src/Core/Services/StateService.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index b7a17e1b8..f4ddb10b1 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -81,7 +81,7 @@ namespace Bit.Core public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}"; public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}"; - public static string UserKeyKey(string userId) => $"userKey_{userId}"; + public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}"; public static string CiphersKey(string userId) => $"ciphers_{userId}"; public static string FoldersKey(string userId) => $"folders_{userId}"; public static string CollectionsKey(string userId) => $"collections_{userId}"; @@ -93,7 +93,7 @@ namespace Bit.Core public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}"; public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}"; public static string KeyHashKey(string userId) => $"keyHash_{userId}"; - public static string UserKeyPinKey(string userId) => $"userKeyPin_{userId}"; + public static string PinKeyEncryptedUserKeyKey(string userId) => $"pinKeyEncryptedUserKey_{userId}"; public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}"; public static string PassGenHistoryKey(string userId) => $"generatedPasswordHistory_{userId}"; public static string TwoFactorTokenKey(string email) => $"twoFactorToken_{email}"; diff --git a/src/Core/Services/StateService.cs b/src/Core/Services/StateService.cs index 27349421c..dbe57cf4b 100644 --- a/src/Core/Services/StateService.cs +++ b/src/Core/Services/StateService.cs @@ -336,12 +336,12 @@ namespace Bit.Core.Services public async Task GetMasterKeyEncryptedUserKeyAsync(string userId = null) { - return await _storageMediatorService.GetAsync(Constants.UserKeyKey(userId), false); + return await _storageMediatorService.GetAsync(Constants.MasterKeyEncryptedUserKeyKey(userId), false); } public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null) { - await _storageMediatorService.SaveAsync(Constants.UserKeyKey(userId), value, false); + await _storageMediatorService.SaveAsync(Constants.MasterKeyEncryptedUserKeyKey(userId), value, false); } public async Task CanAccessPremiumAsync(string userId = null) @@ -397,13 +397,13 @@ namespace Bit.Core.Services public async Task GetPinKeyEncryptedUserKeyAsync(string userId = null) { - var key = await _storageMediatorService.GetAsync(Constants.UserKeyPinKey(userId), false); + var key = await _storageMediatorService.GetAsync(Constants.PinKeyEncryptedUserKeyKey(userId), false); return key != null ? new EncString(key) : null; } public async Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null) { - await _storageMediatorService.SaveAsync(Constants.UserKeyPinKey(userId), value?.EncryptedString, false); + await _storageMediatorService.SaveAsync(Constants.PinKeyEncryptedUserKeyKey(userId), value?.EncryptedString, false); } public async Task GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null) From e076c9fe04e5ab6a024aa3a4bf3e31ab9d23a182 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 3 Aug 2023 10:49:55 -0400 Subject: [PATCH 20/25] [PM-2713] add auto unlock key to mobile --- .../SettingsPage/SettingsPageViewModel.cs | 1 + src/Core/Abstractions/ICryptoService.cs | 2 + src/Core/Abstractions/IStateService.cs | 5 ++ src/Core/Constants.cs | 1 + src/Core/Services/CryptoService.cs | 71 +++++++++++++--- src/Core/Services/StateService.cs | 80 +++++++++++++------ src/Core/Services/VaultTimeoutService.cs | 20 ++++- 7 files changed, 138 insertions(+), 42 deletions(-) diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index 46ce7a52e..5f75ac3d7 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -323,6 +323,7 @@ namespace Bit.App.Pages } if (oldTimeout != newTimeout) { + await _cryptoService.RefreshKeysAsync(); await Device.InvokeOnMainThreadAsync(BuildList); } } diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 040068b24..b7d03df2d 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -19,6 +19,8 @@ namespace Bit.Core.Abstractions Task MakeUserKeyAsync(); Task ClearUserKeyAsync(string userId = null); Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null); + Task GetAutoUnlockKeyAsync(string userId = null); + Task HasAutoUnlockKeyAsync(string userId = null); Task SetMasterKeyAsync(MasterKey masterKey, string userId = null); Task GetMasterKeyAsync(string userId = null); Task MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig); diff --git a/src/Core/Abstractions/IStateService.cs b/src/Core/Abstractions/IStateService.cs index add556dcf..e98936863 100644 --- a/src/Core/Abstractions/IStateService.cs +++ b/src/Core/Abstractions/IStateService.cs @@ -19,6 +19,8 @@ namespace Bit.Core.Abstractions Task SetMasterKeyAsync(MasterKey value, string userId = null); Task GetMasterKeyEncryptedUserKeyAsync(string userId = null); Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null); + Task GetUserKeyAutoUnlockAsync(string userId = null); + Task SetUserKeyAutoUnlockAsync(string value, string userId = null); Task GetActiveUserIdAsync(); Task GetActiveUserEmailAsync(); Task GetActiveUserCustomDataAsync(Func dataMapper); @@ -190,6 +192,9 @@ namespace Bit.Core.Abstractions Task SetEncKeyEncryptedAsync(string value, string userId = null); [Obsolete("Left for migration purposes")] Task SetKeyEncryptedAsync(string value, string userId = null); + + [Obsolete("Use GetUserKeyAutoUnlock instead, left for migration purposes")] + Task GetKeyEncryptedAsync(string userId = null); [Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")] Task GetKeyDecryptedAsync(string userId = null); } diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index f4ddb10b1..608b1845c 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -82,6 +82,7 @@ namespace Bit.Core public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}"; public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}"; public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}"; + public static string UserKeyAutoUnlockKey(string userId) => $"autoUnlock_{userId}"; public static string CiphersKey(string userId) => $"ciphers_{userId}"; public static string FoldersKey(string userId) => $"folders_{userId}"; public static string CollectionsKey(string userId) => $"collections_{userId}"; diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 5786cb19c..2b61f9e67 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -45,24 +45,15 @@ namespace Bit.Core.Services public async Task RefreshKeysAsync() { - // refresh or clear the pin key + // Refresh or clear additional keys such as + // pin and auto unlock keys await SetUserKeyAsync(await GetUserKeyAsync()); } public async Task SetUserKeyAsync(UserKey userKey, string userId = null) { await _stateService.SetUserKeyAsync(userKey, userId); - - // Refresh the Pin Key if the user has a Pin set - if (await _stateService.GetProtectedPinAsync(userId) != null) - { - await UpdateUserKeyPinAsync(userKey, userId); - } - else - { - await _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId); - await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId); - } + await StoreAdditionalKeysAsync(userKey, userId); } public Task GetUserKeyAsync(string userId = null) @@ -108,6 +99,17 @@ namespace Bit.Core.Services return _stateService.SetMasterKeyEncryptedUserKeyAsync(value, userId); } + public async Task GetAutoUnlockKeyAsync(string userId = null) + { + await MigrateAutoUnlockKeyIfNeededAsync(userId); + return await _stateService.GetUserKeyAutoUnlockAsync(userId); + } + + public async Task HasAutoUnlockKeyAsync(string userId = null) + { + return (await GetAutoUnlockKeyAsync(userId) != null); + } + public Task SetMasterKeyAsync(MasterKey masterKey, string userId = null) { return _stateService.SetMasterKeyAsync(masterKey, userId); @@ -666,6 +668,30 @@ namespace Bit.Core.Services // --HELPER METHODS-- + private async Task StoreAdditionalKeysAsync(UserKey userKey, string userId = null) + { + // Refresh, set, or clear the pin key + if (await _stateService.GetProtectedPinAsync(userId) != null) + { + await UpdateUserKeyPinAsync(userKey, userId); + } + else + { + await _stateService.SetPinKeyEncryptedUserKeyAsync(null, userId); + await _stateService.SetPinKeyEncryptedUserKeyEphemeralAsync(null, userId); + } + + // Refresh, set, or clear the auto key + if (await _stateService.GetVaultTimeoutAsync(userId) == null) + { + await _stateService.SetUserKeyAutoUnlockAsync(userKey.KeyB64, userId); + } + else + { + await _stateService.SetUserKeyAutoUnlockAsync(null, userId); + } + } + private async Task UpdateUserKeyPinAsync(UserKey userKey, string userId = null) { var pin = await DecryptToUtf8Async(new EncString(await _stateService.GetProtectedPinAsync(userId))); @@ -930,6 +956,27 @@ namespace Bit.Core.Services // 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. + private async Task MigrateAutoUnlockKeyIfNeededAsync(string userId = null) + { + var oldAutoKey = await _stateService.GetKeyEncryptedAsync(userId); + if (oldAutoKey == null) + { + return; + } + // Decrypt + var masterKey = new MasterKey(Convert.FromBase64String(oldAutoKey)); + var encryptedUserKey = await _stateService.GetEncKeyEncryptedAsync(userId); + var userKey = await DecryptUserKeyWithMasterKeyAsync( + masterKey, + new EncString(encryptedUserKey), + userId); + // Migrate + await _stateService.SetUserKeyAutoUnlockAsync(userKey.KeyB64, userId); + await _stateService.SetKeyEncryptedAsync(null, userId); + // Set encrypted user key just in case the user locks without syncing + await SetMasterKeyEncryptedUserKeyAsync(encryptedUserKey); + } + public async Task DecryptAndMigrateOldPinKeyAsync( bool masterPasswordOnRestart, string pin, diff --git a/src/Core/Services/StateService.cs b/src/Core/Services/StateService.cs index dbe57cf4b..77d51833c 100644 --- a/src/Core/Services/StateService.cs +++ b/src/Core/Services/StateService.cs @@ -336,12 +336,27 @@ namespace Bit.Core.Services public async Task GetMasterKeyEncryptedUserKeyAsync(string userId = null) { - return await _storageMediatorService.GetAsync(Constants.MasterKeyEncryptedUserKeyKey(userId), false); + return await _storageMediatorService.GetAsync( + await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), false); } public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null) { - await _storageMediatorService.SaveAsync(Constants.MasterKeyEncryptedUserKeyKey(userId), value, false); + await _storageMediatorService.SaveAsync( + await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), value, false); + } + + public async Task GetUserKeyAutoUnlockAsync(string userId = null) + { + var keyB64 = await _storageMediatorService.GetAsync( + await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), true); + return keyB64 == null ? null : new UserKey(Convert.FromBase64String(keyB64)); + } + + public async Task SetUserKeyAutoUnlockAsync(string value, string userId = null) + { + await _storageMediatorService.SaveAsync( + await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), value, true); } public async Task CanAccessPremiumAsync(string userId = null) @@ -350,6 +365,7 @@ namespace Bit.Core.Services { userId = await GetActiveUserIdAsync(); } + if (!await IsAuthenticatedAsync(userId)) { return false; @@ -397,13 +413,15 @@ namespace Bit.Core.Services public async Task GetPinKeyEncryptedUserKeyAsync(string userId = null) { - var key = await _storageMediatorService.GetAsync(Constants.PinKeyEncryptedUserKeyKey(userId), false); + var key = await _storageMediatorService.GetAsync( + await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), false); return key != null ? new EncString(key) : null; } public async Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null) { - await _storageMediatorService.SaveAsync(Constants.PinKeyEncryptedUserKeyKey(userId), value?.EncryptedString, false); + await _storageMediatorService.SaveAsync( + await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), value?.EncryptedString, false); } public async Task GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null) @@ -1445,28 +1463,30 @@ namespace Bit.Core.Services } // Non-state storage - await SetProtectedPinAsync(null, userId); - await SetPinProtectedAsync(null, userId); - await SetKeyEncryptedAsync(null, userId); - await SetKeyHashAsync(null, userId); - await SetEncKeyEncryptedAsync(null, userId); - await SetOrgKeysEncryptedAsync(null, userId); - await SetPrivateKeyEncryptedAsync(null, userId); - await SetLastActiveTimeAsync(null, userId); - await SetPreviousPageInfoAsync(null, userId); - await SetInvalidUnlockAttemptsAsync(null, userId); - await SetLocalDataAsync(null, userId); - await SetEncryptedCiphersAsync(null, userId); - await SetEncryptedCollectionsAsync(null, userId); - await SetLastSyncAsync(null, userId); - await SetEncryptedFoldersAsync(null, userId); - await SetEncryptedPoliciesAsync(null, userId); - await SetUsesKeyConnectorAsync(null, userId); - await SetOrganizationsAsync(null, userId); - await SetEncryptedPasswordGenerationHistoryAsync(null, userId); - await SetEncryptedSendsAsync(null, userId); - await SetSettingsAsync(null, userId); - await SetApprovePasswordlessLoginsAsync(null, userId); + await Task.WhenAll( + SetUserKeyAutoUnlockAsync(null, userId), + SetProtectedPinAsync(null, userId), + SetKeyHashAsync(null, userId), + SetOrgKeysEncryptedAsync(null, userId), + SetPrivateKeyEncryptedAsync(null, userId), + SetLastActiveTimeAsync(null, userId), + SetPreviousPageInfoAsync(null, userId), + SetInvalidUnlockAttemptsAsync(null, userId), + SetLocalDataAsync(null, userId), + SetEncryptedCiphersAsync(null, userId), + SetEncryptedCollectionsAsync(null, userId), + SetLastSyncAsync(null, userId), + SetEncryptedFoldersAsync(null, userId), + SetEncryptedPoliciesAsync(null, userId), + SetUsesKeyConnectorAsync(null, userId), + SetOrganizationsAsync(null, userId), + SetEncryptedPasswordGenerationHistoryAsync(null, userId), + SetEncryptedSendsAsync(null, userId), + SetSettingsAsync(null, userId), + SetApprovePasswordlessLoginsAsync(null, userId), + SetEncKeyEncryptedAsync(null, userId), + SetKeyEncryptedAsync(null, userId), + SetPinProtectedAsync(null, userId)); } private async Task ScaffoldNewAccountAsync(Account account) @@ -1713,6 +1733,14 @@ namespace Bit.Core.Services await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions); } + [Obsolete("Use GetUserKeyAutoUnlock instead, left for migration purposes")] + public async Task GetKeyEncryptedAsync(string userId = null) + { + var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId }, + await GetDefaultSecureStorageOptionsAsync()); + return await GetValueAsync(Constants.KeyKey(reconciledOptions.UserId), reconciledOptions); + } + [Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")] public async Task GetKeyDecryptedAsync(string userId = null) { diff --git a/src/Core/Services/VaultTimeoutService.cs b/src/Core/Services/VaultTimeoutService.cs index 28639aeea..28b48c258 100644 --- a/src/Core/Services/VaultTimeoutService.cs +++ b/src/Core/Services/VaultTimeoutService.cs @@ -59,15 +59,26 @@ namespace Bit.Core.Services public async Task IsLockedAsync(string userId = null) { - var hasKey = await _cryptoService.HasUserKeyAsync(userId); - if (hasKey) + var biometricSet = await IsBiometricLockSetAsync(userId); + if (biometricSet && await _stateService.GetBiometricLockedAsync(userId)) { - var biometricSet = await IsBiometricLockSetAsync(userId); - if (biometricSet && await _stateService.GetBiometricLockedAsync(userId)) + return true; + } + + if (!await _cryptoService.HasUserKeyAsync(userId)) + { + if (await _cryptoService.HasAutoUnlockKeyAsync(userId)) + { + await _cryptoService.SetUserKeyAsync(await _cryptoService.GetAutoUnlockKeyAsync(userId)); + } + else { return true; } } + + // Check again to verify auto key was set + var hasKey = await _cryptoService.HasUserKeyAsync(userId); return !hasKey; } @@ -196,6 +207,7 @@ namespace Bit.Core.Services await Task.WhenAll( _cryptoService.ClearUserKeyAsync(userId), _cryptoService.ClearMasterKeyAsync(userId), + _stateService.SetUserKeyAutoUnlockAsync(null, userId), _cryptoService.ClearOrgKeysAsync(true, userId), _cryptoService.ClearKeyPairAsync(true, userId)); From 4fa8d2ba2857cbee7b8f12ea987d4eab5666ce42 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 3 Aug 2023 13:30:45 -0400 Subject: [PATCH 21/25] [PM-2713] set user key on set password page --- src/App/Pages/Accounts/SetPasswordPageViewModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs index a933988a9..2f0966786 100644 --- a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs +++ b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs @@ -195,6 +195,7 @@ namespace Bit.App.Pages // Set Password and relevant information await _apiService.SetPasswordAsync(request); await _stateService.SetKdfConfigurationAsync(kdfConfig); + await _cryptoService.SetUserKeyAsync(newUserKey); await _cryptoService.SetMasterKeyAsync(newMasterKey); await _cryptoService.SetMasterKeyHashAsync(localMasterPasswordHash); await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString); From 270a395d9fa3763e4c6c61859b49a6ccb4c7fc0b Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 3 Aug 2023 13:41:19 -0400 Subject: [PATCH 22/25] [PM-2713] set enc user key during kc onboarding --- src/Core/Services/AuthService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index e6a8e9517..a5a686ebc 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -520,11 +520,13 @@ namespace Bit.Core.Services tokenResponse.KdfConfig); var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64); - await _cryptoService.SetMasterKeyAsync(newMasterKey); var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey); await _cryptoService.SetUserKeyAsync(newUserKey); + await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(newProtectedUserKey.EncryptedString); + await _cryptoService.SetMasterKeyAsync(newMasterKey); + var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(); try From c595b1626ed7bc1f646999d298417b6e4a1797b6 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 3 Aug 2023 14:34:50 -0400 Subject: [PATCH 23/25] fix formatting --- src/Core/Services/CryptoService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 2b61f9e67..a5971a1ac 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -107,9 +107,9 @@ namespace Bit.Core.Services public async Task HasAutoUnlockKeyAsync(string userId = null) { - return (await GetAutoUnlockKeyAsync(userId) != null); + return await GetAutoUnlockKeyAsync(userId) != null; } - + public Task SetMasterKeyAsync(MasterKey masterKey, string userId = null) { return _stateService.SetMasterKeyAsync(masterKey, userId); From e820408a64addeb7178bed5de0a9e1c323e8828f Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 3 Aug 2023 15:44:41 -0400 Subject: [PATCH 24/25] [PM-2713] make method async again - returning null from a task thats not async throws --- src/Core/Services/CryptoService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index a5971a1ac..e0eaa8e8f 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -625,13 +625,13 @@ namespace Bit.Core.Services encString.Iv, encString.Mac, key); } - public Task EncryptAsync(string plainValue, SymmetricCryptoKey key = null) + public async Task EncryptAsync(string plainValue, SymmetricCryptoKey key = null) { if (plainValue == null) { return null; } - return EncryptAsync(Encoding.UTF8.GetBytes(plainValue), key); + return await EncryptAsync(Encoding.UTF8.GetBytes(plainValue), key); } public async Task EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null) From 78004dbdb9a8d4abe0046fadd46e53e3428231f6 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 3 Aug 2023 16:28:04 -0400 Subject: [PATCH 25/25] [PM-2713] clear service cache when adding new account --- src/App/Utilities/AccountManagement/AccountsManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App/Utilities/AccountManagement/AccountsManager.cs b/src/App/Utilities/AccountManagement/AccountsManager.cs index 919070359..6fffba58c 100644 --- a/src/App/Utilities/AccountManagement/AccountsManager.cs +++ b/src/App/Utilities/AccountManagement/AccountsManager.cs @@ -206,6 +206,7 @@ namespace Bit.App.Utilities.AccountManagement private async Task AddAccountAsync() { + await AppHelpers.ClearServiceCacheAsync(); await Device.InvokeOnMainThreadAsync(() => { Options.HideAccountSwitcher = false;