private async Task RoundTrip() { ReaderResult initialHeaderResult = await this.reader.ReadHeaderAsync(await this.thisTestInfo.Database.AsIStorageFile.OpenReadAsync(), CancellationToken.None); Assert.AreEqual(ReaderResult.Success, initialHeaderResult, "Initial header read should be successful"); KdbxDecryptionResult result = await this.reader.DecryptFileAsync(await this.thisTestInfo.Database.AsIStorageFile.OpenReadAsync(), this.thisTestInfo.Password, this.thisTestInfo.Keyfile, CancellationToken.None); Assert.AreEqual(ReaderResult.Success, result.Result, "File should have initially decrypted properly"); KdbxDocument kdbxDoc = result.GetDocument(); IKdbxWriter writer = this.reader.GetWriter(); using (var stream = new InMemoryRandomAccessStream()) { bool writeResult = await writer.WriteAsync(stream, kdbxDoc, CancellationToken.None); Assert.IsTrue(writeResult, "File should have written successfully"); stream.Seek(0); KdbxReader newReader = new KdbxReader(); ReaderResult result2 = await newReader.ReadHeaderAsync(stream, CancellationToken.None); Assert.AreEqual(ReaderResult.Success, result2, "Header should been read back successfully after write"); KdbxDecryptionResult result3 = await newReader.DecryptFileAsync(stream, this.thisTestInfo.Password, this.thisTestInfo.Keyfile, CancellationToken.None); Assert.AreEqual(ReaderResult.Success, result3.Result, "File should have decrypted successfully after write"); KdbxDocument roundTrippedDocument = result3.GetDocument(); Assert.AreEqual(kdbxDoc, roundTrippedDocument, "Round-tripped document should be equal to original document"); } }
public async Task Initialize() { CancellationTokenSource cts = new CancellationTokenSource(); this.clipboardService = new SensitiveClipboardService(this.clipboard); try { Utils.DatabaseInfo databaseInfo = await Utils.GetDatabaseInfoForTest(TestContext); KdbxReader reader = new KdbxReader(); using (IRandomAccessStream stream = await databaseInfo.Database.AsIStorageFile.OpenReadAsync()) { Assert.IsFalse((await reader.ReadHeaderAsync(stream, cts.Token)).IsError); KdbxDecryptionResult decryption = await reader.DecryptFileAsync(stream, databaseInfo.Password, databaseInfo.Keyfile, cts.Token); Assert.IsFalse(decryption.Result.IsError); this.viewModel = new DatabaseViewModel( decryption.GetDocument(), new MockResourceProvider(), reader.HeaderData.GenerateRng(), new DatabaseNavigationViewModel(), new DummyPersistenceService(), new AppSettingsService(new InMemorySettingsProvider()), this.clipboardService ); await this.viewModel.ActivateAsync(); } } catch (InvalidOperationException) { } }
public async Task Initialize() { CancellationTokenSource cts = new CancellationTokenSource(); MethodInfo testMethod = GetType().GetRuntimeMethod( TestContext.TestName, new Type[0] ); var specAttr = testMethod.GetCustomAttribute <DetailsForAttribute>(); var dataAttr = testMethod.GetCustomAttribute <TestDataAttribute>(); Assert.IsTrue(specAttr != null || dataAttr != null); try { Utils.DatabaseInfo databaseInfo = await Utils.GetDatabaseInfoForTest(TestContext); KdbxReader reader = new KdbxReader(); using (IRandomAccessStream stream = await databaseInfo.Database.AsIStorageFile.OpenReadAsync()) { Assert.IsFalse((await reader.ReadHeaderAsync(stream, cts.Token)).IsError); KdbxDecryptionResult decryption = await reader.DecryptFileAsync(stream, databaseInfo.Password, databaseInfo.Keyfile, cts.Token); Assert.IsFalse(decryption.Result.IsError); this.document = decryption.GetDocument(); if (specAttr != null && (dataAttr == null || !dataAttr.SkipInitialization)) { IDatabaseNavigationViewModel navVm = new DatabaseNavigationViewModel(); navVm.SetGroup(this.document.Root.DatabaseGroup); IDatabasePersistenceService persistenceService = new DummyPersistenceService(); this.instantiationTime = DateTime.Now; if (specAttr.IsNew) { this.expectedParent = this.document.Root.DatabaseGroup; this.viewModel = GetNewViewModel(navVm, persistenceService, this.document, this.expectedParent); } else { this.expectedParent = this.document.Root.DatabaseGroup; this.viewModel = GetExistingViewModel( navVm, persistenceService, this.document, specAttr.IsOpenedReadOnly ); } } else { this.expectedParent = null; Assert.IsTrue(dataAttr.SkipInitialization); } } } catch (InvalidOperationException) { } }
public async Task ChangePassword() { string newPw = "TestPW"; this.masterKeyVm.MasterPassword = newPw; Assert.IsFalse(this.masterKeyVm.ConfirmCommand.CanExecute(null), "Should not be able to confirm new password until password is entered twice"); this.masterKeyVm.ConfirmedPassword = newPw; Assert.IsTrue(this.masterKeyVm.ConfirmCommand.CanExecute(null), "Should be able to confirm new password when second password matches"); this.masterKeyVm.ConfirmedPassword = "******"; Assert.IsFalse(this.masterKeyVm.ConfirmCommand.CanExecute(null), "Mismatched passwords should not be able to be confirmed"); this.masterKeyVm.ConfirmedPassword = newPw; DateTime lastPasswordChange = this.document.Metadata.MasterKeyChanged.Value; this.masterKeyVm.ConfirmCommand.Execute(null); DateTime passwordChangeTime = DateTime.Parse(this.document.Metadata.MasterKeyChanged.Value.ToString()); Assert.IsTrue(passwordChangeTime > lastPasswordChange, "MasterKeyChanged value should have changed in document metadata"); Assert.IsTrue(await this.persistenceService.Save(this.document)); KdbxReader reader = new KdbxReader(); using (IRandomAccessStream stream = await this.saveFile.AsIStorageFile.OpenReadAsync()) { await reader.ReadHeaderAsync(stream, CancellationToken.None); KdbxDecryptionResult decryption = await reader.DecryptFileAsync(stream, newPw, null, CancellationToken.None); Assert.AreEqual(KdbxParserCode.Success, decryption.Result.Code, "Database should decrypt with the new credentials"); KdbxDocument document = decryption.GetDocument(); Assert.AreEqual(passwordChangeTime, document.Metadata.MasterKeyChanged.Value, "MasterKeyChanged timestamp should have been persisted"); } }
public async Task DowngradeCipherSettings() { DateTime lastPasswordChange = this.document.Metadata.MasterKeyChanged.Value; Assert.AreEqual(EncryptionAlgorithm.ChaCha20, this.settingsVm.Cipher, "ChaCha20 should be the encryption algorithm before the test starts"); this.writer.Cipher = EncryptionAlgorithm.Aes; Assert.IsInstanceOfType(this.settingsVm.GetKdfParameters(), typeof(Argon2Parameters), "Argon2 should be the KDF before the test starts according to the VM"); Assert.IsInstanceOfType(this.writer.KdfParameters, typeof(Argon2Parameters), "Argon2 should be the KDF before the test starts according to the KdbxWriter"); this.settingsVm.KdfGuid = AesParameters.AesUuid; this.settingsVm.KdfIterations = 6001; Assert.IsInstanceOfType(this.writer.KdfParameters, typeof(AesParameters), "Changes to the settings VM should be reflected in the KdbxWriter"); Assert.IsTrue(await this.persistenceService.Save(this.document)); KdbxReader reader = new KdbxReader(); using (IRandomAccessStream stream = await this.saveFile.AsIStorageFile.OpenReadAsync()) { await reader.ReadHeaderAsync(stream, CancellationToken.None); Assert.AreEqual(EncryptionAlgorithm.Aes, reader.HeaderData.Cipher, "New reader should have the correct cipher"); AesParameters aesParams = reader.HeaderData.KdfParameters as AesParameters; Assert.IsNotNull(aesParams, "Database should have properly persisted with AES"); Assert.AreEqual(6001, (int)aesParams.Rounds, "AES iteration count should have been persisted correctly"); KdbxDecryptionResult decryption = await reader.DecryptFileAsync(stream, this.dbPassword, this.dbKeyFile, CancellationToken.None); Assert.AreEqual(KdbxParserCode.Success, decryption.Result.Code); KdbxDocument document = decryption.GetDocument(); Assert.AreEqual(lastPasswordChange, document.Metadata.MasterKeyChanged.Value, "MasterKeyChanged timestamp should not have changed"); } }
public async Task Init() { // Get database from test attributes Utils.DatabaseInfo dbInfo = await Utils.GetDatabaseInfoForTest(TestContext); this.dbPassword = dbInfo.Password; this.dbKeyFile = dbInfo.Keyfile; // Assert that databases named *ReadOnly* are actually readonly after a clone if (dbInfo.Database.Name.IndexOf("ReadOnly", StringComparison.OrdinalIgnoreCase) >= 0) { Assert.IsFalse( await dbInfo.Database.CheckWritableAsync(), $"This file is expected to be read-only; please verify this before testing: {dbInfo.Database.Name}" ); } this.saveFile = (await dbInfo.Database.AsIStorageFile.CopyAsync( ApplicationData.Current.TemporaryFolder, $"PersistenceTestDb-{Guid.NewGuid()}.kdbx", NameCollisionOption.ReplaceExisting )).AsWrapper(); // Use a KdbxReader to parse the database and get a corresponding writer KdbxReader reader = new KdbxReader(); using (IRandomAccessStream stream = await this.saveFile.AsIStorageFile.OpenReadAsync()) { await reader.ReadHeaderAsync(stream, CancellationToken.None); KdbxDecryptionResult decryption = await reader.DecryptFileAsync(stream, dbInfo.Password, dbInfo.Keyfile, CancellationToken.None); Assert.AreEqual(KdbxParserCode.Success, decryption.Result.Code); this.document = decryption.GetDocument(); } // Construct services we can use for the test this.writer = reader.GetWriter(); this.persistenceService = new DefaultFilePersistenceService( this.writer, this.writer, new StorageFileDatabaseCandidate(this.saveFile, true), new MockSyncContext(), true ); this.credentialStorage = new MockCredentialProvider(); this.masterKeyVm = new MasterKeyChangeViewModel( this.document, this.saveFile, new DatabaseCredentialProvider(this.persistenceService, this.credentialStorage), new MockFileService() ); this.settingsVm = new DatabaseSettingsViewModel(this.writer); }
public async Task Initialize() { Utils.DatabaseInfo databaseInfo = await Utils.GetDatabaseInfoForTest(TestContext); KdbxReader reader = new KdbxReader(); using (IRandomAccessStream stream = await databaseInfo.Database.AsIStorageFile.OpenReadAsync()) { Assert.IsFalse((await reader.ReadHeaderAsync(stream, CancellationToken.None)).IsError); KdbxDecryptionResult decryption = await reader.DecryptFileAsync(stream, databaseInfo.Password, databaseInfo.Keyfile, CancellationToken.None); Assert.IsFalse(decryption.Result.IsError); this.document = decryption.GetDocument(); this.rng = reader.HeaderData.GenerateRng(); } }
private async Task ExpectUnlockError(KdbxParserCode error, bool expectIdentical = true) { CancellationTokenSource cts = new CancellationTokenSource(); KdbxDecryptionResult result = await this.reader.DecryptFileAsync(await this.thisTestInfo.Database.AsIStorageFile.OpenReadAsync(), this.thisTestInfo.Password, this.thisTestInfo.Keyfile, cts.Token); if (result.Result == ReaderResult.Success) { KdbxDocument oldDocument = result.GetDocument(); XElement newXml = oldDocument.ToXml(this.reader.HeaderData.GenerateRng(), result.Parameters); KdbxDocument newDocument = new KdbxDocument( newXml, this.reader.HeaderData.ProtectedBinaries, this.reader.HeaderData.GenerateRng(), result.Parameters ); Assert.AreEqual(oldDocument, newDocument); } Assert.AreEqual(error, result.Result.Code); }
public async Task Initialize() { try { CancellationTokenSource cts = new CancellationTokenSource(); Utils.DatabaseInfo databaseInfo = await Utils.GetDatabaseInfoForTest(TestContext); KdbxReader reader = new KdbxReader(); using (IRandomAccessStream stream = await databaseInfo.Database.AsIStorageFile.OpenReadAsync()) { Assert.IsFalse((await reader.ReadHeaderAsync(stream, cts.Token)).IsError); KdbxDecryptionResult decryption = await reader.DecryptFileAsync(stream, databaseInfo.Password, databaseInfo.Keyfile, cts.Token); Assert.IsFalse(decryption.Result.IsError); this.document = decryption.GetDocument(); } } catch (InvalidOperationException) { } this.viewModel = new DatabaseNavigationViewModel(); }
/// <summary> /// Attempts to unlock the document file. /// </summary> /// <param name="storedCredential">The key to use for decryption - if null, the ViewModel's /// credentials are used instead.</param> private async Task DoUnlockAsync(IBuffer storedCredential) { DebugHelper.Assert(CanUnlock()); if (!CanUnlock()) { throw new InvalidOperationException("The ViewModel is not in a state that can unlock the database!"); } CancellationTokenSource cts = new CancellationTokenSource(); try { using (IRandomAccessStream stream = await CandidateFile.GetRandomReadAccessStreamAsync()) { Task <KdbxDecryptionResult> decryptionTask; if (storedCredential != null) { decryptionTask = this.kdbxReader.DecryptFile(stream, storedCredential, cts.Token); } else { decryptionTask = this.kdbxReader.DecryptFileAsync(stream, Password, KeyFile, cts.Token); } if (this.taskNotificationService.CurrentTask == null || this.taskNotificationService.CurrentTask.IsCompleted) { this.taskNotificationService.PushOperation(decryptionTask, cts, AsyncOperationType.DatabaseDecryption); } KdbxDecryptionResult result = await decryptionTask; ParseResult = result.Result; DebugHelper.Trace($"Got ParseResult from database unlock attempt: {ParseResult}"); if (!ParseResult.IsError) { // The database candidate to proceed into the next stage with IDatabaseCandidate candidateToUse = CandidateFile; if (CacheDatabase) { // We do not use UseAppControlledDatabaseAsync here because it has extra baggage. // We don't need to refresh the view at this stage, just fire an event using // the cached file. candidateToUse = await GetCachedCandidateAsync(); } if (RememberDatabase) { string accessToken = this.futureAccessList.Add(candidateToUse.File, candidateToUse.FileName); DebugHelper.Trace($"Unlock was successful and database was remembered with token: {accessToken}"); } else { DebugHelper.Trace("Unlock was successful but user opted not to remember the database."); } if (SaveCredentials) { bool storeCredential = false; // If we were not already using a stored credential, we need user // consent to continue. if (storedCredential == null) { Task <bool> identityTask = this.identityService.VerifyIdentityAsync(); if (this.taskNotificationService.CurrentTask == null || this.taskNotificationService.CurrentTask.IsCompleted) { this.taskNotificationService.PushOperation(identityTask, AsyncOperationType.IdentityVerification); } storeCredential = await identityTask; storedCredential = result.GetRawKey(); } else { // If we have a stored credential, we already got consent. storeCredential = true; } if (storeCredential) { if (!await this.credentialProvider.TryStoreRawKeyAsync(candidateToUse.File, storedCredential)) { EventHandler <CredentialStorageFailureEventArgs> handler = CredentialStorageFailed; if (handler != null) { // If we could not store a credential, give the View a chance to try again. CredentialStorageFailureEventArgs eventArgs = new CredentialStorageFailureEventArgs( this.credentialProvider, this.credentialViewModelFactory, candidateToUse, storedCredential ); handler(this, eventArgs); await eventArgs.DeferAsync(); } } } } await RaiseDocumentReady(result.GetDocument(), candidateToUse); } } } catch (COMException) { // In the Windows 8.1 preview, opening a stream to a SkyDrive file can fail with no workaround. ParseResult = new ReaderResult(KdbxParserCode.UnableToReadFile); } }
public async Task MultiEdit_Degenerate() { StorageFileDatabaseCandidateFactory factory = new StorageFileDatabaseCandidateFactory(new MockFileProxyProvider { ScopeValue = true }); StorageFolder work = await Utils.GetWorkFolder(); IDatabaseCandidate workDb = await factory.AssembleAsync( (await this.thisTestInfo.Database.AsIStorageFile.CopyAsync(work, "Work.kdbx", NameCollisionOption.ReplaceExisting)) .AsWrapper() ); IKdbxWriter writer; KdbxDocument doc; var reader = new KdbxReader(); using (IRandomAccessStream stream = await workDb.GetRandomReadAccessStreamAsync()) { ReaderResult headerResult = await reader.ReadHeaderAsync(stream, CancellationToken.None); Assert.AreEqual(headerResult, ReaderResult.Success); } KdbxDecryptionResult bodyResult = null; using (IRandomAccessStream stream = await workDb.GetRandomReadAccessStreamAsync()) { bodyResult = await reader.DecryptFileAsync(stream, this.thisTestInfo.Password, this.thisTestInfo.Keyfile, CancellationToken.None); Assert.AreEqual(bodyResult.Result, ReaderResult.Success); } writer = reader.GetWriter(); doc = bodyResult.GetDocument(); IDatabasePersistenceService persistor = new DefaultFilePersistenceService(writer, writer, workDb, new MockSyncContext(), await workDb.File.CheckWritableAsync()); Assert.IsTrue(persistor.CanSave); Assert.IsTrue(await persistor.Save(doc)); // Remove the last group doc.Root.DatabaseGroup.Children.RemoveAt( doc.Root.DatabaseGroup.Children.IndexOf( doc.Root.DatabaseGroup.Children.Last(node => node is IKeePassGroup) ) ); Assert.IsTrue(await persistor.Save(doc)); reader = new KdbxReader(); using (IRandomAccessStream stream = await workDb.GetRandomReadAccessStreamAsync()) { ReaderResult headerResult = await reader.ReadHeaderAsync(stream, CancellationToken.None); Assert.AreEqual(headerResult, ReaderResult.Success); } using (IRandomAccessStream stream = await workDb.GetRandomReadAccessStreamAsync()) { bodyResult = await reader.DecryptFileAsync(stream, this.thisTestInfo.Password, this.thisTestInfo.Keyfile, CancellationToken.None); Assert.AreEqual(bodyResult.Result, ReaderResult.Success); } writer = reader.GetWriter(); doc = bodyResult.GetDocument(); doc.Root.DatabaseGroup.Children.RemoveAt( doc.Root.DatabaseGroup.Children.IndexOf( doc.Root.DatabaseGroup.Children.Last(node => node is IKeePassGroup) ) ); Assert.IsTrue(await persistor.Save(doc)); reader = new KdbxReader(); using (IRandomAccessStream stream = await workDb.GetRandomReadAccessStreamAsync()) { ReaderResult headerResult = await reader.ReadHeaderAsync(stream, CancellationToken.None); Assert.AreEqual(headerResult, ReaderResult.Success); } using (IRandomAccessStream stream = await workDb.GetRandomReadAccessStreamAsync()) { bodyResult = await reader.DecryptFileAsync(stream, this.thisTestInfo.Password, this.thisTestInfo.Keyfile, CancellationToken.None); Assert.AreEqual(bodyResult.Result, ReaderResult.Success); } writer = reader.GetWriter(); doc = bodyResult.GetDocument(); }