Beispiel #1
0
        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) { }
        }
Beispiel #3
0
        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);
        }
Beispiel #7
0
        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();
            }
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
        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();
        }
Beispiel #10
0
        /// <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);
            }
        }
Beispiel #11
0
        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();
        }