using Bit.Core.Abstractions; using Bit.Core.Models.Domain; using Bit.Core.Models.View; using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Bit.Core.Services { public class CipherService { private const string Keys_CiphersFormat = "ciphers_{0}"; private const string Keys_LocalData = "ciphersLocalData"; private const string Keys_NeverDomains = "neverDomains"; private List _decryptedCipherCache; private readonly ICryptoService _cryptoService; private readonly IUserService _userService; private readonly ISettingsService _settingsService; private readonly IApiService _apiService; private readonly IStorageService _storageService; private readonly II18nService _i18NService; private Dictionary> _domainMatchBlacklist = new Dictionary> { ["google.com"] = new HashSet { "script.google.com" } }; public CipherService( ICryptoService cryptoService, IUserService userService, ISettingsService settingsService, IApiService apiService, IStorageService storageService, II18nService i18nService) { _cryptoService = cryptoService; _userService = userService; _settingsService = settingsService; _apiService = apiService; _storageService = storageService; _i18NService = i18nService; } private List DecryptedCipherCache { get => _decryptedCipherCache; set { if(value == null) { _decryptedCipherCache.Clear(); } _decryptedCipherCache = value; // TODO: update search index } } public void ClearCache() { DecryptedCipherCache = null; } public async Task Encrypt(CipherView model, SymmetricCryptoKey key = null, Cipher originalCipher = null) { // Adjust password history if(model.Id != null) { // TODO } var cipher = new Cipher(); cipher.Id = model.Id; // TODO others if(key == null && cipher.OrganizationId != null) { key = await _cryptoService.GetOrgKeyAsync(cipher.OrganizationId); if(key == null) { throw new Exception("Cannot encrypt cipher for organization. No key."); } } var tasks = new List(); tasks.Add(EncryptObjPropertyAsync(model, cipher, new HashSet { nameof(model.Name) }, key)); await Task.WhenAll(tasks); return cipher; } private Task EncryptObjPropertyAsync(V model, D obj, HashSet map, SymmetricCryptoKey key) where V : View where D : Domain { var modelType = model.GetType(); var objType = obj.GetType(); Task makeCs(string propName) { var modelPropInfo = modelType.GetProperty(propName); var modelProp = modelPropInfo.GetValue(model) as string; if(!string.IsNullOrWhiteSpace(modelProp)) { return _cryptoService.EncryptAsync(modelProp, key); } return Task.FromResult((CipherString)null); }; void setCs(string propName, CipherString val) { var objPropInfo = objType.GetProperty(propName); objPropInfo.SetValue(obj, val, null); }; var tasks = new List(); foreach(var prop in map) { tasks.Add(makeCs(prop).ContinueWith(async val => setCs(prop, await val))); } return Task.WhenAll(tasks); } } }