/// <summary> /// Loads the given database file using the provided password /// </summary> /// <param name="databaseFile"></param> /// <param name="password"></param> /// <returns></returns> public async Task LoadDatabaseAsync(StorageFile databaseFile, PasswordParameter password) { _databaseFile = databaseFile; IsLoading = true; try { // Open the database (may trigger exceptions) await openDatabaseAsync(databaseFile, password); DatabaseOpened = true; // And only if everything went fine, store the tokens and database names // Store the selected file token in the future access list to easily retrieve it later string token = StorageApplicationPermissions.FutureAccessList.Add(databaseFile); // Add or replace the token value for the last database file ApplicationData.Current.LocalSettings.Values[FUTURE_LIST_DATABASE_TOKEN_KEY] = token; ApplicationData.Current.LocalSettings.Values[LAST_DATABASE_NAME_KEY] = databaseFile.Name; } finally { IsLoading = false; } }
/// <summary> /// Save the given credentials in the os password vault /// </summary> /// <param name="password"></param> private static void StoreDatabasePassword(string databaseName, PasswordParameter password) { var vault = new Windows.Security.Credentials.PasswordVault(); vault.Add( new PasswordCredential(databaseName, PASSWORD_VAULT_USER_NAME, password.Password)); }
private async static Task <PasswordParameter> ShowPasswordDialogAsync(string databaseName) { // Builds a custom dialog with a text field PasswordDialog pwdDialog = new PasswordDialog(databaseName); // The result is PasswordParameter pwd = new PasswordParameter(); await pwdDialog.ShowAsync(); if (pwdDialog.Result) { if (pwdDialog.ViewModel.UsePassword) { pwd.Password = pwdDialog.ViewModel.Password; } if (pwdDialog.ViewModel.UseKeyFile && pwdDialog.ViewModel.KeyFile != null) { pwd.KeyFile = pwdDialog.ViewModel.KeyFile; } // User validated the password return(pwd); } else { throw new TaskCanceledException(); } }
/// <summary> /// Asks the user to selecte a database /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void OpenDatabaseButton_Click(object sender, RoutedEventArgs e) { var picker = new FileOpenPicker(); picker.FileTypeFilter.Add(".kdbx"); string databaseName = String.Empty; try { var file = await picker.PickSingleFileAsync(); if (file != null) { databaseName = file.Name; // Ask for password PasswordParameter password = await DatabasePasswordProvider.GetDatabasePasswordAsync(databaseName); // Then load the database await ViewModel.LoadDatabaseAsync(file, password); // Once loaded, navigate to the collection page if (SettingsViewModel.Instance.ViewCollectionAsTree) { this.Frame.Navigate(typeof(EntryCollectionTreeViewPage)); } else { this.Frame.Navigate(typeof(EntryCollectionPage)); } } } catch (WinKeeLib.Keys.InvalidCompositeKeyException) { DatabasePasswordProvider.ClearSavedPasswordForDatabase(databaseName); MessageDialog err = new MessageDialog( ResourceLoader.GetForCurrentView().GetString("MessageDialog_IncorrectKey")); await err.ShowAsync(); } catch (TaskCanceledException) { // User cancelled password entering } catch (Exception ex) { // Error opening the database MessageDialog err = new MessageDialog( ResourceLoader.GetForCurrentView().GetString("MessageDialog_ErrorOpeningDatabase") + "\n\"" + ex.Message + "\""); await err.ShowAsync(); } }
private async Task LoadPreviousDatabaseAsync() { try { // Ask for password PasswordParameter password = await DatabasePasswordProvider.GetDatabasePasswordAsync(ViewModel.LastDatabaseName); // Then load the database await ViewModel.LoadPreviouslyOpenedDatabase(password); // Once loaded, navigate to the collection page if (SettingsViewModel.Instance.ViewCollectionAsTree) { this.Frame.Navigate(typeof(EntryCollectionTreeViewPage)); } else { this.Frame.Navigate(typeof(EntryCollectionPage)); } } catch (WinKeeLib.Keys.InvalidCompositeKeyException) { // Invalid key, ensure it is not saved DatabasePasswordProvider.ClearSavedPasswordForDatabase(ViewModel.LastDatabaseName); MessageDialog err = new MessageDialog( ResourceLoader.GetForCurrentView().GetString("MessageDialog_IncorrectKey")); await err.ShowAsync(); } catch (TaskCanceledException) { // User cancelled password entering } catch (Exception ex) { // Error opening the database MessageDialog err = new MessageDialog( ResourceLoader.GetForCurrentView().GetString("MessageDialog_ErrorOpeningDatabase") + "\n\"" + ex.Message + "\""); await err.ShowAsync(); } }
/// <summary> /// Loads the previously opened database, if possible, using the provided password /// </summary> /// <param name="password"></param> /// <returns></returns> public async Task LoadPreviouslyOpenedDatabase(PasswordParameter password) { // Read token in settings object value = String.Empty; bool success = ApplicationData.Current.LocalSettings.Values.TryGetValue(FUTURE_LIST_DATABASE_TOKEN_KEY, out value); if (success) { string token = (string)value; StorageFile file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(token); if (file != null) { await LoadDatabaseAsync(file, password); } } else { throw new FileNotFoundException("Database file not found"); } }
/// <summary> /// Opens the given database using the provided password, and the optional keyfile /// </summary> /// <param name="databaseFile"></param> /// <param name="password"></param> /// <param name="keyFile"></param> /// <returns></returns> private async Task openDatabaseAsync(StorageFile databaseFile, PasswordParameter password) { try { using (var stream = await databaseFile.OpenReadAsync()) { // Build up the database var db = new PwDatabase(); var key = new CompositeKey(); if (!String.IsNullOrEmpty(password.Password)) { // Add the password key.AddUserKey(new KcpPassword(password.Password)); } if (password.KeyFile != null) { var keyFileStream = await password.KeyFile.OpenReadAsync(); if (keyFileStream != null) { key.AddUserKey(new KcpKeyFile(keyFileStream.AsStreamForRead())); } } var cancelToken = new CancellationTokenSource(); db.Open(stream.GetInputStreamAt(0).AsStreamForRead(), key, null, cancelToken.Token); // Now iterate all groups entries to create the ViewModel counterpart if (db.RootGroup != null) { if (SettingsViewModel.Instance.ViewCollectionAsTree) { _groups.Add(new PwGroupViewModel(db.RootGroup, true)); } else { _groups.Add(new PwGroupViewModel(db.RootGroup)); foreach (var group in db.RootGroup.GetGroups(false)) { _groups.Add(new PwGroupViewModel(group, true)); } } } if (SettingsViewModel.Instance.ViewCollectionAsTree) { // Create the hierarchical group foreach (var groupVM in _groups) { addHierachicalGroup(groupVM, 0); } if (_groups.Count > 0) { CurrentGroup = _groups.FirstOrDefault(); } } if (SettingsViewModel.Instance.SortingEnabled) { // Sort groups by name _groups = new ObservableCollection <PwGroupViewModel>(_groups.OrderBy(g => g.Name)); } // Initialize the filtered list instances UpdateFilterededEntries(String.Empty); db.Close(); } } catch (InvalidCompositeKeyException) { throw; } catch (Exception) { throw; } }
/// <summary> /// Retrieve the password to use to open the given database /// </summary> /// <param name="databaseName">name of the database</param> /// <returns>credentials to use to open this database</returns> public async static Task <PasswordParameter> GetDatabasePasswordAsync(string databaseName) { PasswordParameter credentials = null; // Try to use Windows Hello if (SettingsViewModel.Instance.WindowsHelloEnabled && await KeyCredentialManager.IsSupportedAsync() ) { // Try to open an already generated Windows Hello credentials var passportCredentials = await KeyCredentialManager.OpenAsync(WINDOWS_HELLO_SERVICE_NAME); if (passportCredentials.Status == KeyCredentialStatus.Success) { var signRes = await passportCredentials.Credential.RequestSignAsync( CryptographicBuffer.ConvertStringToBinary("LoginAuth", BinaryStringEncoding.Utf8)); if (signRes.Status == KeyCredentialStatus.Success) { // User confirmed its identity, retrieve the credentials from the system Password Vault try { credentials = await LoadDatabasePasswordAsync(databaseName); } catch (Exception) { // Prompt for credentials, then store them PasswordParameter password = await ShowPasswordDialogAsync(databaseName); StoreDatabasePassword(databaseName, password); credentials = password; } } else if (signRes.Status == KeyCredentialStatus.UserCanceled) { // User cancelled the credential throw new TaskCanceledException(); } } else { // No credentials available, need to create it var passportCredentialCreation = await KeyCredentialManager.RequestCreateAsync(WINDOWS_HELLO_SERVICE_NAME, KeyCredentialCreationOption.FailIfExists); if (passportCredentialCreation.Status == KeyCredentialStatus.Success || passportCredentialCreation.Status == KeyCredentialStatus.CredentialAlreadyExists) { // Prompt for credentials, then store them PasswordParameter password = await ShowPasswordDialogAsync(databaseName); StoreDatabasePassword(databaseName, password); credentials = password; } else if (passportCredentialCreation.Status == KeyCredentialStatus.UserCanceled) { // User cancelled the credential throw new TaskCanceledException(); } } } if (credentials == null) { // No Windows Hello, use standard password credentials = await ShowPasswordDialogAsync(databaseName); } return(credentials); }