From 35aebaa3e02e5777866ddef240efc44e9ec9daaf Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Thu, 10 Jun 2021 09:41:28 +1000 Subject: [PATCH] Use 2 iterations for local password hashing --- src/App/Pages/Accounts/LockPageViewModel.cs | 9 +++++---- .../Pages/Accounts/SetPasswordPageViewModel.cs | 5 +++-- .../Pages/Settings/ExportVaultPageViewModel.cs | 2 +- .../Services/MobilePasswordRepromptService.cs | 2 +- src/Core/Abstractions/ICryptoService.cs | 2 +- src/Core/Services/AuthService.cs | 18 +++++++++++------- src/Core/Services/CryptoService.cs | 2 +- .../Controllers/LockPasswordViewController.cs | 8 ++++---- 8 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index fcd148858..811da6cba 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -241,25 +241,26 @@ namespace Bit.App.Pages else { var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations); - var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key); + var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization); var passwordValid = false; - if (keyHash != null) + if (localKeyHash != null) { var storedKeyHash = await _cryptoService.GetKeyHashAsync(); if (storedKeyHash != null) { - passwordValid = storedKeyHash == keyHash; + passwordValid = storedKeyHash == localKeyHash; } else { await _deviceActionService.ShowLoadingAsync(AppResources.Loading); + var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization); var request = new PasswordVerificationRequest(); request.MasterPasswordHash = keyHash; try { await _apiService.PostAccountVerifyPasswordAsync(request); passwordValid = true; - await _cryptoService.SetKeyHashAsync(keyHash); + await _cryptoService.SetKeyHashAsync(localKeyHash); } catch (Exception e) { diff --git a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs index e523e7354..78f850017 100644 --- a/src/App/Pages/Accounts/SetPasswordPageViewModel.cs +++ b/src/App/Pages/Accounts/SetPasswordPageViewModel.cs @@ -138,7 +138,8 @@ namespace Bit.App.Pages var kdfIterations = 100000; var email = await _userService.GetEmailAsync(); var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations); - var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key); + var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization); + var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization); Tuple encKey; var existingEncKey = await _cryptoService.GetEncKeyAsync(); @@ -174,7 +175,7 @@ namespace Bit.App.Pages await _userService.SetInformationAsync(await _userService.GetUserIdAsync(), await _userService.GetEmailAsync(), kdf, kdfIterations); await _cryptoService.SetKeyAsync(key); - await _cryptoService.SetKeyHashAsync(masterPasswordHash); + await _cryptoService.SetKeyHashAsync(localMasterPasswordHash); await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString); await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString); await _deviceActionService.HideLoadingAsync(); diff --git a/src/App/Pages/Settings/ExportVaultPageViewModel.cs b/src/App/Pages/Settings/ExportVaultPageViewModel.cs index cafda15da..71fc9e6ae 100644 --- a/src/App/Pages/Settings/ExportVaultPageViewModel.cs +++ b/src/App/Pages/Settings/ExportVaultPageViewModel.cs @@ -103,7 +103,7 @@ namespace Bit.App.Pages return; } - var keyHash = await _cryptoService.HashPasswordAsync(_masterPassword, null); + var keyHash = await _cryptoService.HashPasswordAsync(_masterPassword, null, Core.Enums.HashPurpose.LocalAuthorization); MasterPassword = string.Empty; var storedKeyHash = await _cryptoService.GetKeyHashAsync(); diff --git a/src/App/Services/MobilePasswordRepromptService.cs b/src/App/Services/MobilePasswordRepromptService.cs index f6cab89e7..44f590ff0 100644 --- a/src/App/Services/MobilePasswordRepromptService.cs +++ b/src/App/Services/MobilePasswordRepromptService.cs @@ -29,7 +29,7 @@ namespace Bit.App.Services return false; }; - var keyHash = await _cryptoService.HashPasswordAsync(password, null); + var keyHash = await _cryptoService.HashPasswordAsync(password, null, Core.Enums.HashPurpose.LocalAuthorization); var storedKeyHash = await _cryptoService.GetKeyHashAsync(); if (storedKeyHash == null || keyHash == null || storedKeyHash != keyHash) diff --git a/src/Core/Abstractions/ICryptoService.cs b/src/Core/Abstractions/ICryptoService.cs index 2c605f034..fbc0c1c26 100644 --- a/src/Core/Abstractions/ICryptoService.cs +++ b/src/Core/Abstractions/ICryptoService.cs @@ -31,7 +31,7 @@ namespace Bit.Core.Abstractions Task GetPrivateKeyAsync(); Task GetPublicKeyAsync(); Task HasEncKeyAsync(); - Task HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose? hashPurpose); + Task HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); Task HasKeyAsync(); Task> MakeEncKeyAsync(SymmetricCryptoKey key); Task MakeKeyAsync(string password, string salt, KdfType? kdf, int? kdfIterations); diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index 0cf15f90f..7a34110ed 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -93,6 +93,7 @@ namespace Bit.Core.Services public string Email { get; set; } public string MasterPasswordHash { get; set; } + public string LocalMasterPasswordHash { get; set; } public string Code { get; set; } public string CodeVerifier { get; set; } public string SsoRedirectUrl { get; set; } @@ -123,7 +124,8 @@ namespace Bit.Core.Services SelectedTwoFactorProviderType = null; var key = await MakePreloginKeyAsync(masterPassword, email); var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key); - return await LogInHelperAsync(email, hashedPassword, null, null, null, key, null, null, null); + var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization); + return await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, null, null, null); } public async Task LogInSsoAsync(string code, string codeVerifier, string redirectUrl) @@ -135,7 +137,7 @@ namespace Bit.Core.Services public Task LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken, bool? remember = null) { - return LogInHelperAsync(Email, MasterPasswordHash, Code, CodeVerifier, SsoRedirectUrl, _key, + return LogInHelperAsync(Email, MasterPasswordHash, LocalMasterPasswordHash, Code, CodeVerifier, SsoRedirectUrl, _key, twoFactorProvider, twoFactorToken, remember); } @@ -145,7 +147,8 @@ namespace Bit.Core.Services SelectedTwoFactorProviderType = null; var key = await MakePreloginKeyAsync(masterPassword, email); var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key); - return await LogInHelperAsync(email, hashedPassword, null, null, null, key, twoFactorProvider, + var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization); + return await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, twoFactorProvider, twoFactorToken, remember); } @@ -153,7 +156,7 @@ namespace Bit.Core.Services TwoFactorProviderType twoFactorProvider, string twoFactorToken, bool? remember = null) { SelectedTwoFactorProviderType = null; - return await LogInHelperAsync(null, null, code, codeVerifier, redirectUrl, null, twoFactorProvider, + return await LogInHelperAsync(null, null, null, code, codeVerifier, redirectUrl, null, twoFactorProvider, twoFactorToken, remember); } @@ -266,8 +269,8 @@ namespace Bit.Core.Services return await _cryptoService.MakeKeyAsync(masterPassword, email, kdf, kdfIterations); } - private async Task LogInHelperAsync(string email, string hashedPassword, string code, - string codeVerifier, string redirectUrl, SymmetricCryptoKey key, + private async Task LogInHelperAsync(string email, string hashedPassword, string localHashedPassword, + string code, string codeVerifier, string redirectUrl, SymmetricCryptoKey key, TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null) { var storedTwoFactorToken = await _tokenService.GetTwoFactorTokenAsync(email); @@ -318,6 +321,7 @@ namespace Bit.Core.Services var twoFactorResponse = response.Item2; Email = email; MasterPasswordHash = hashedPassword; + LocalMasterPasswordHash = localHashedPassword; Code = code; CodeVerifier = codeVerifier; SsoRedirectUrl = redirectUrl; @@ -339,7 +343,7 @@ namespace Bit.Core.Services if (_setCryptoKeys) { await _cryptoService.SetKeyAsync(key); - await _cryptoService.SetKeyHashAsync(hashedPassword); + await _cryptoService.SetKeyHashAsync(localHashedPassword); await _cryptoService.SetEncKeyAsync(tokenResponse.Key); // User doesn't have a key pair yet (old account), let's generate one for them. diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 374f66132..2947ba609 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -433,7 +433,7 @@ namespace Bit.Core.Services return new SymmetricCryptoKey(sendKey); } - public async Task HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose? hashPurpose) + public async Task HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization) { if (key == null) { diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs index 4c0a59b9c..28228661a 100644 --- a/src/iOS.Core/Controllers/LockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -175,7 +175,7 @@ namespace Bit.iOS.Core.Controllers else { var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdf, kdfIterations); - var keyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2); + var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization); var storedKeyHash = await _cryptoService.GetKeyHashAsync(); if (storedKeyHash == null) { @@ -183,11 +183,11 @@ namespace Bit.iOS.Core.Controllers if (key2.KeyB64 == oldKey) { await _secureStorageService.RemoveAsync("oldKey"); - await _cryptoService.SetKeyHashAsync(keyHash); - storedKeyHash = keyHash; + await _cryptoService.SetKeyHashAsync(localKeyHash); + storedKeyHash = localKeyHash; } } - if (storedKeyHash != null && keyHash != null && storedKeyHash == keyHash) + if (storedKeyHash != null && localKeyHash != null && storedKeyHash == localKeyHash) { if (_pinSet.Item1) {