diff --git a/src/App/Abstractions/Services/ICryptoService.cs b/src/App/Abstractions/Services/ICryptoService.cs index e33816207..d59e2835e 100644 --- a/src/App/Abstractions/Services/ICryptoService.cs +++ b/src/App/Abstractions/Services/ICryptoService.cs @@ -8,11 +8,10 @@ namespace Bit.App.Abstractions public interface ICryptoService { SymmetricCryptoKey Key { get; set; } - SymmetricCryptoKey PreviousKey { get; } - bool KeyChanged { get; } + SymmetricCryptoKey EncKey { get; } byte[] PrivateKey { get; } IDictionary OrgKeys { get; } - + void SetEncKey(CipherString encKeyEnc); void SetPrivateKey(CipherString privateKeyEnc); void SetOrgKeys(ProfileResponse profile); void SetOrgKeys(Dictionary orgKeysEncDict); @@ -26,5 +25,6 @@ namespace Bit.App.Abstractions string MakeKeyFromPasswordBase64(string password, string salt); byte[] HashPassword(SymmetricCryptoKey key, string password); string HashPasswordBase64(SymmetricCryptoKey key, string password); + CipherString MakeEncKey(SymmetricCryptoKey key); } } \ No newline at end of file diff --git a/src/App/Models/Api/Request/RegisterRequest.cs b/src/App/Models/Api/Request/RegisterRequest.cs index 5f37f1e64..618ec0bb0 100644 --- a/src/App/Models/Api/Request/RegisterRequest.cs +++ b/src/App/Models/Api/Request/RegisterRequest.cs @@ -6,5 +6,6 @@ public string Email { get; set; } public string MasterPasswordHash { get; set; } public string MasterPasswordHint { get; set; } + public string Key { get; set; } } } diff --git a/src/App/Models/Api/Response/ProfileResponse.cs b/src/App/Models/Api/Response/ProfileResponse.cs index 1a537e517..e2bc1a6b4 100644 --- a/src/App/Models/Api/Response/ProfileResponse.cs +++ b/src/App/Models/Api/Response/ProfileResponse.cs @@ -10,6 +10,8 @@ namespace Bit.App.Models.Api public string MasterPasswordHint { get; set; } public string Culture { get; set; } public bool TwoFactorEnabled { get; set; } + public string Key { get; set; } + public string PrivateKey { get; set; } public IEnumerable Organizations { get; set; } } } diff --git a/src/App/Models/Api/Response/TokenResponse.cs b/src/App/Models/Api/Response/TokenResponse.cs index b033101f7..450f053b3 100644 --- a/src/App/Models/Api/Response/TokenResponse.cs +++ b/src/App/Models/Api/Response/TokenResponse.cs @@ -15,5 +15,6 @@ namespace Bit.App.Models.Api public string TokenType { get; set; } public List TwoFactorProviders { get; set; } public string PrivateKey { get; set; } + public string Key { get; set; } } } diff --git a/src/App/Models/CipherString.cs b/src/App/Models/CipherString.cs index ea435e4ec..cebf6c12a 100644 --- a/src/App/Models/CipherString.cs +++ b/src/App/Models/CipherString.cs @@ -83,18 +83,13 @@ namespace Bit.App.Models } EncryptionType = encryptionType; - EncryptedString = string.Format("{0}|{1}", initializationVector, cipherText); + EncryptedString = string.Format("{0}.{1}|{2}", (byte)EncryptionType, initializationVector, cipherText); if(!string.IsNullOrWhiteSpace(mac)) { EncryptedString = string.Format("{0}|{1}", EncryptedString, mac); } - if(EncryptionType != EncryptionType.AesCbc256_B64) - { - EncryptedString = string.Format("{0}.{1}", (byte)EncryptionType, EncryptedString); - } - CipherText = cipherText; InitializationVector = initializationVector; Mac = mac; diff --git a/src/App/Pages/RegisterPage.cs b/src/App/Pages/RegisterPage.cs index b0f8987f9..fe625e315 100644 --- a/src/App/Pages/RegisterPage.cs +++ b/src/App/Pages/RegisterPage.cs @@ -203,12 +203,14 @@ namespace Bit.App.Pages var normalizedEmail = EmailCell.Entry.Text.ToLower(); var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, normalizedEmail); + var encKey = _cryptoService.MakeEncKey(key); var request = new RegisterRequest { Email = normalizedEmail, MasterPasswordHash = _cryptoService.HashPasswordBase64(key, PasswordCell.Entry.Text), MasterPasswordHint = !string.IsNullOrWhiteSpace(PasswordHintCell.Entry.Text) - ? PasswordHintCell.Entry.Text : null + ? PasswordHintCell.Entry.Text : null, + Key = encKey.EncryptedString }; _userDialogs.ShowLoading(AppResources.CreatingAccount, MaskType.Black); diff --git a/src/App/Pages/Vault/VaultListLoginsPage.cs b/src/App/Pages/Vault/VaultListLoginsPage.cs index 77ab081e7..4b6dd1158 100644 --- a/src/App/Pages/Vault/VaultListLoginsPage.cs +++ b/src/App/Pages/Vault/VaultListLoginsPage.cs @@ -31,7 +31,6 @@ namespace Bit.App.Pages private readonly ISettings _settings; private readonly IGoogleAnalyticsService _googleAnalyticsService; private readonly bool _favorites; - private bool _loadExistingData; private CancellationTokenSource _filterResultsCancellationTokenSource; public VaultListLoginsPage(bool favorites, string uri = null) @@ -50,7 +49,6 @@ namespace Bit.App.Pages _googleAnalyticsService = Resolver.Resolve(); var cryptoService = Resolver.Resolve(); - _loadExistingData = !_settings.GetValueOrDefault(Constants.FirstVaultLoad, true) || !cryptoService.KeyChanged; Uri = uri; @@ -234,10 +232,7 @@ namespace Bit.App.Pages Search.SearchButtonPressed += SearchBar_SearchButtonPressed; AddLoginItem?.InitEvents(); - if(_loadExistingData) - { - _filterResultsCancellationTokenSource = FetchAndLoadVault(); - } + _filterResultsCancellationTokenSource = FetchAndLoadVault(); if(_connectivity.IsConnected && Device.RuntimePlatform == Device.iOS && !_favorites) { @@ -307,9 +302,6 @@ namespace Bit.App.Pages private CancellationTokenSource FetchAndLoadVault() { var cts = new CancellationTokenSource(); - _settings.AddOrUpdateValue(Constants.FirstVaultLoad, false); - _loadExistingData = true; - if(PresentationFolders.Count > 0 && _syncService.SyncInProgress) { return cts; diff --git a/src/App/Services/AuthService.cs b/src/App/Services/AuthService.cs index 258523adb..c96e78a08 100644 --- a/src/App/Services/AuthService.cs +++ b/src/App/Services/AuthService.cs @@ -6,8 +6,6 @@ using Bit.App.Models.Api; using Plugin.Settings.Abstractions; using Bit.App.Models; using System.Linq; -using System.Collections.Generic; -using System.Diagnostics; namespace Bit.App.Services { @@ -273,6 +271,11 @@ namespace Bit.App.Services private async Task ProcessLoginSuccessAsync(SymmetricCryptoKey key, TokenResponse response) { + if(response.Key != null) + { + _cryptoService.SetEncKey(new CipherString(response.PrivateKey)); + } + if(response.PrivateKey != null) { _cryptoService.SetPrivateKey(new CipherString(response.PrivateKey)); diff --git a/src/App/Services/CryptoService.cs b/src/App/Services/CryptoService.cs index c6e42cb37..4e5316b93 100644 --- a/src/App/Services/CryptoService.cs +++ b/src/App/Services/CryptoService.cs @@ -16,8 +16,8 @@ namespace Bit.App.Services public class CryptoService : ICryptoService { private const string KeyKey = "key"; - private const string PreviousKeyKey = "previousKey"; private const string PrivateKeyKey = "encPrivateKey"; + private const string EncKeyKey = "encKey"; private const string OrgKeysKey = "encOrgKeys"; private const int InitializationVectorSize = 16; @@ -25,6 +25,7 @@ namespace Bit.App.Services private readonly ISecureStorageService _secureStorage; private readonly IKeyDerivationService _keyDerivationService; private SymmetricCryptoKey _key; + private SymmetricCryptoKey _encKey; private SymmetricCryptoKey _legacyEtmKey; private SymmetricCryptoKey _previousKey; private IDictionary _orgKeys; @@ -63,7 +64,6 @@ namespace Bit.App.Services } else { - PreviousKey = _key; _secureStorage.Delete(KeyKey); } @@ -72,46 +72,27 @@ namespace Bit.App.Services } } - public SymmetricCryptoKey PreviousKey + public SymmetricCryptoKey EncKey { get { - if(_previousKey == null && _secureStorage.Contains(PreviousKeyKey)) + if(_encKey == null && _settings.Contains(EncKeyKey)) { - var keyBytes = _secureStorage.Retrieve(PreviousKeyKey); - if(keyBytes != null) + var encKey = _settings.GetValueOrDefault(EncKeyKey); + var encKeyCs = new CipherString(encKey); + try { - _previousKey = new SymmetricCryptoKey(keyBytes); + var decBytes = DecryptToBytes(encKeyCs, Key); + _encKey = new SymmetricCryptoKey(decBytes); + } + catch + { + _encKey = null; + Debug.WriteLine($"Cannot set enc key. Decryption failed."); } } - return _previousKey; - } - private set - { - if(value != null) - { - _secureStorage.Store(PreviousKeyKey, value.Key); - _previousKey = value; - } - } - } - - public bool KeyChanged - { - get - { - if(Key == null) - { - return false; - } - - if(PreviousKey == null) - { - return Key != null; - } - - return !PreviousKey.Key.SequenceEqual(Key.Key); + return _encKey; } } @@ -169,6 +150,20 @@ namespace Bit.App.Services } } + public void SetEncKey(CipherString encKeyEnc) + { + if(encKeyEnc != null) + { + _settings.AddOrUpdateValue(EncKeyKey, encKeyEnc.EncryptedString); + } + else if(_settings.Contains(EncKeyKey)) + { + _settings.Remove(EncKeyKey); + } + + _encKey = null; + } + public void SetPrivateKey(CipherString privateKeyEnc) { if(privateKeyEnc != null) @@ -178,12 +173,9 @@ namespace Bit.App.Services else if(_settings.Contains(PrivateKeyKey)) { _settings.Remove(PrivateKeyKey); - _privateKey = null; - } - else - { - _privateKey = null; } + + _privateKey = null; } public void SetOrgKeys(ProfileResponse profile) @@ -234,13 +226,25 @@ namespace Bit.App.Services SetOrgKeys((Dictionary)null); Key = null; SetPrivateKey(null); + SetEncKey(null); } public CipherString Encrypt(string plaintextValue, SymmetricCryptoKey key = null) + { + if(plaintextValue == null) + { + throw new ArgumentNullException(nameof(plaintextValue)); + } + + var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue); + return Encrypt(plaintextBytes, key); + } + + public CipherString Encrypt(byte[] plainBytes, SymmetricCryptoKey key = null) { if(key == null) { - key = Key; + key = EncKey ?? Key; } if(key == null) @@ -248,17 +252,15 @@ namespace Bit.App.Services throw new ArgumentNullException(nameof(key)); } - if(plaintextValue == null) + if(plainBytes == null) { - throw new ArgumentNullException(nameof(plaintextValue)); + throw new ArgumentNullException(nameof(plainBytes)); } - var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue); - var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); var cryptoKey = provider.CreateSymmetricKey(key.EncKey); var iv = WinRTCrypto.CryptographicBuffer.GenerateRandom(provider.BlockLength); - var encryptedBytes = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plaintextBytes, iv); + var encryptedBytes = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plainBytes, iv); var mac = key.MacKey != null ? ComputeMacBase64(encryptedBytes, iv, key.MacKey) : null; return new CipherString(key.EncryptionType, Convert.ToBase64String(iv), @@ -283,7 +285,7 @@ namespace Bit.App.Services { if(key == null) { - key = Key; + key = EncKey ?? Key; } if(key == null) @@ -448,7 +450,7 @@ namespace Bit.App.Services { if(key == null) { - throw new ArgumentNullException(nameof(Key)); + throw new ArgumentNullException(nameof(key)); } if(password == null) @@ -466,5 +468,11 @@ namespace Bit.App.Services var hash = HashPassword(key, password); return Convert.ToBase64String(hash); } + + public CipherString MakeEncKey(SymmetricCryptoKey key) + { + var bytes = WinRTCrypto.CryptographicBuffer.GenerateRandom(512 / 8); + return Encrypt(bytes, key); + } } } diff --git a/src/App/Services/SyncService.cs b/src/App/Services/SyncService.cs index bdd2bfdce..5081a7fce 100644 --- a/src/App/Services/SyncService.cs +++ b/src/App/Services/SyncService.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using Xamarin.Forms; using Newtonsoft.Json; using Bit.App.Models; -using System.Diagnostics; namespace Bit.App.Services { @@ -203,7 +202,7 @@ namespace Bit.App.Services return false; } - await SyncOrgKeysAsync(profile.Result); + await SyncProfileKeysAsync(profile.Result); SyncCompleted(true); return true; @@ -258,11 +257,11 @@ namespace Bit.App.Services var loginTask = SyncLoginsAsync(loginsDict); var folderTask = SyncFoldersAsync(foldersDict); var domainsTask = SyncDomainsAsync(domains.Result); - var orgKeysTask = SyncOrgKeysAsync(profile.Result); - await Task.WhenAll(loginTask, folderTask, domainsTask, orgKeysTask).ConfigureAwait(false); + var profileTask = SyncProfileKeysAsync(profile.Result); + await Task.WhenAll(loginTask, folderTask, domainsTask, profileTask).ConfigureAwait(false); if(folderTask.Exception != null || loginTask.Exception != null || domainsTask.Exception != null || - orgKeysTask.Exception != null) + profileTask.Exception != null) { SyncCompleted(false); return false; @@ -397,23 +396,20 @@ namespace Bit.App.Services catch(SQLite.SQLiteException) { } } - private async Task SyncOrgKeysAsync(ProfileResponse profile) + private Task SyncProfileKeysAsync(ProfileResponse profile) { - if(_cryptoService.PrivateKey == null && (profile.Organizations?.Any() ?? false)) + if(!string.IsNullOrWhiteSpace(profile.Key)) { - var keys = await _accountsApiRepository.GetKeys(); - if(!CheckSuccess(keys)) - { - return; - } + _cryptoService.SetEncKey(new CipherString(profile.Key)); + } - if(!string.IsNullOrWhiteSpace(keys.Result.PrivateKey)) - { - _cryptoService.SetPrivateKey(new CipherString(keys.Result.PrivateKey)); - } + if(!string.IsNullOrWhiteSpace(profile.PrivateKey)) + { + _cryptoService.SetPrivateKey(new CipherString(profile.PrivateKey)); } _cryptoService.SetOrgKeys(profile); + return Task.FromResult(0); } private void SyncStarted()