using System; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using Windows.Storage; using Microsoft.HockeyApp; using ModernKeePass.Domain.Exceptions; using ModernKeePass.Interfaces; using ModernKeePass.ViewModels; using ModernKeePassLib; using ModernKeePassLib.Cryptography.KeyDerivation; using ModernKeePassLib.Interfaces; using ModernKeePassLib.Keys; using ModernKeePassLib.Serialization; namespace ModernKeePass.Services { public class DatabaseService: SingletonServiceBase, IDatabaseService { private readonly PwDatabase _pwDatabase = new PwDatabase(); private readonly ISettingsService _settings; private StorageFile _databaseFile; private GroupVm _recycleBin; private CompositeKey _compositeKey; public GroupVm RootGroup { get; set; } public GroupVm RecycleBin { get { return _recycleBin; } set { _recycleBin = value; _pwDatabase.RecycleBinUuid = _recycleBin?.IdUuid; } } public string Name => _databaseFile?.Name; public bool RecycleBinEnabled { get { return _pwDatabase.RecycleBinEnabled; } set { _pwDatabase.RecycleBinEnabled = value; } } public PwUuid DataCipher { get { return _pwDatabase.DataCipherUuid; } set { _pwDatabase.DataCipherUuid = value; } } public PwCompressionAlgorithm CompressionAlgorithm { get { return _pwDatabase.Compression; } set { _pwDatabase.Compression = value; } } public KdfParameters KeyDerivation { get { return _pwDatabase.KdfParameters; } set { _pwDatabase.KdfParameters = value; } } public bool IsOpen => _pwDatabase.IsOpen; public bool IsFileOpen => !_pwDatabase.IsOpen && _databaseFile != null; public bool IsClosed => _databaseFile == null; public bool HasChanged { get; set; } public DatabaseService() : this(SettingsService.Instance) { } public DatabaseService(ISettingsService settings) { _settings = settings; } /// /// Open a KeePass database /// /// The database file /// The database composite key /// True to create a new database before opening it /// An error message, if any public async Task Open(StorageFile databaseFile, CompositeKey key, bool createNew = false) { try { if (databaseFile == null) { throw new ArgumentNullException(nameof(databaseFile)); } if (key == null) { throw new ArgumentNullException(nameof(key)); } _compositeKey = key; var fileContents = await FileIO.ReadBufferAsync(databaseFile); var ioConnection = IOConnectionInfo.FromByteArray(fileContents.ToArray()); if (createNew) { _pwDatabase.New(ioConnection, key); var fileFormat = _settings.GetSetting("DefaultFileFormat"); switch (fileFormat) { case "4": KeyDerivation = KdfPool.Get("Argon2").GetDefaultParameters(); break; } } else _pwDatabase.Open(ioConnection, key, new NullStatusLogger()); _databaseFile = databaseFile; RootGroup = new GroupVm(_pwDatabase.RootGroup, null, RecycleBinEnabled ? _pwDatabase.RecycleBinUuid : null); } catch (InvalidCompositeKeyException ex) { HockeyClient.Current.TrackException(ex); throw new ArgumentException(ex.Message, ex); } } public async Task ReOpen() { await Open(_databaseFile, _compositeKey); } /// /// Commit the changes to the currently opened database to file /// public void Save() { if (!IsOpen) return; try { _pwDatabase.Save(new NullStatusLogger()); } catch (Exception e) { throw new SaveException(e); } } /// /// Save the current database to another file and open it /// /// The new database file public async Task Save(StorageFile file) { var oldFile = _databaseFile; _databaseFile = file; try { var fileContents = await FileIO.ReadBufferAsync(file); _pwDatabase.SaveAs(IOConnectionInfo.FromByteArray(fileContents.ToArray()), true, new NullStatusLogger()); } catch { _databaseFile = oldFile; throw; } } /// /// Close the currently opened database /// public void Close(bool releaseFile = true) { _pwDatabase?.Close(); if (releaseFile) _databaseFile = null; } public void AddDeletedItem(PwUuid id) { _pwDatabase.DeletedObjects.Add(new PwDeletedObject(id, DateTime.UtcNow)); } public void CreateRecycleBin(string title) { RecycleBin = RootGroup.AddNewGroup(title); RecycleBin.IsSelected = true; RecycleBin.IconId = (int)PwIcon.TrashBin; } public void UpdateCompositeKey(CompositeKey newCompositeKey) { if (newCompositeKey == null) return; _compositeKey = newCompositeKey; _pwDatabase.MasterKey = newCompositeKey; } } }