/// <summary> /// Parses the header of the CandidateFile and handles updating status on the View. /// </summary> private async Task ValidateHeader() { DebugHelper.Assert(this.kdbxReader != null); if (this.kdbxReader == null) { throw new InvalidOperationException("Cannot validate KDBX header if there is no reader instance"); } try { if (CandidateFile == null) { ParseResult = null; } else { using (IRandomAccessStream fileStream = await CandidateFile.GetRandomReadAccessStreamAsync()) { CancellationTokenSource cts = new CancellationTokenSource(5000); ParseResult = await this.kdbxReader.ReadHeaderAsync(fileStream, cts.Token); } } } 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); } finally { RaiseHeaderValidated(); UnlockCommand.RaiseCanExecuteChanged(); UseSavedCredentialsCommand.RaiseCanExecuteChanged(); } }
/// <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); } }