/// <summary> /// Unlocks the database prompted to unlock on the KeyPromptForm /// </summary> /// <param name="ioInfo">IOConnectionInfo that represents the database.</param> /// <param name="keyPromptForm">KeyPromptForm to unlock the database from.</param> internal async static void UnlockDatabase(IOConnectionInfo ioInfo, KeyPromptForm keyPromptForm) { // Only one try is allowed if (WinHelloUnlockExt.tries < 1) { if (KeePass.Program.Config.Security.MasterKeyOnSecureDesktop) { WinHelloUnlockExt.secureChaged = true; WinHelloUnlockExt.isMonitoring = true; var _ = Task.Factory.StartNew(() => CloseWarning()); KeePass.Program.Config.Security.MasterKeyOnSecureDesktop = false; UWPLibrary.Unlock(keyPromptForm, new CompositeKey()); } else { await UWPLibrary.UnlockDatabase(ioInfo, keyPromptForm); ++WinHelloUnlockExt.tries; if (WinHelloUnlockExt.secureChaged) { KeePass.Program.Config.Security.MasterKeyOnSecureDesktop = true; WinHelloUnlockExt.secureChaged = false; } } } else { keyPromptForm.Visible = true; keyPromptForm.Opacity = 1; } WinHelloUnlockExt.opened = true; }
/// <summary> /// Called everytime a database is opened. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void FileOpenedHandler(object sender, FileOpenedEventArgs e) { var ioInfo = e.Database.IOConnectionInfo; if (e.Database.CustomData.Get(ProductName) == null) // If there is no CustomData in this database { // Create CustomData to save global setting to enable or disable the plugin e.Database.CustomData.Set(ProductName, "true"); e.Database.Modified = true; // Try to save the database try { e.Database.Save(null); } catch { } } // Global settings to be used in the Options Panel dbName = Library.CharChange(ioInfo.Path); database = e.Database; UWPLibrary.ck = database.MasterKey; if (e.Database.CustomData.Get(ProductName + "AT") == "true") { LockAfterAutoType = true; } else { LockAfterAutoType = false; } if (e.Database.CustomData.Get(ProductName) == "true") { enablePlugin = true; } if (e.Database.CustomData.Get(ProductName) == "false") // if plugin is disabled for the database { enablePlugin = false; return; // Don't do anything else } if (await UWPLibrary.FirstTime(dbName)) // If the database has no credentials saved { bool isHelloAvailable = await UWPLibrary.IsHelloAvailable(); if (isHelloAvailable) { // Ask the user if he/she wants to configure the plugin bool yesOrNo = MessageService.AskYesNo("Do You want to set " + WinHelloUnlockExt.ProductName + " for " + dbName + " now?", WinHelloUnlockExt.ShortProductName, true); // In case he/she wants, create the credentials if (yesOrNo) { await UWPLibrary.CreateHelloData(dbName); } } } // Set global settings back to default tries = 0; opened = true; }
/// <summary> /// Used to detect if the master key has been changed /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void OnSavedDB(Object sender, FileSavedEventArgs args) { var db = args.Database; var ioInfo = db.IOConnectionInfo; string dbPath = Library.CharChange(ioInfo.Path); if (!await UWPLibrary.FirstTime(dbPath) && await UWPLibrary.IsHelloAvailable() && !Library.CheckMasterKey(ioInfo, UWPLibrary.ck)) { await Library.HandleMasterKeyChange(ioInfo, dbPath, false); } }
/// <summary>Updates the form when the CheckBox is clicked.</summary> private void CheckBox_Change(object sender, EventArgs e) { bool check = checkBox.Checked; first = Task.Run(() => UWPLibrary.FirstTime(db)).Result; checkBox1.Enabled = check; createButton.Enabled = first && check; deleteButton.Enabled = !first; label1.Text = ""; if (!check && !first) { label1.Text = "WinHelloUnlock Data will be deleted"; } }
/// <summary>Register for the FormClosing event.</summary> protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (ParentForm != null) { // Save the settings on FormClosing. ParentForm.FormClosing += delegate(object sender2, FormClosingEventArgs e2) { if (ParentForm.DialogResult == DialogResult.OK) { WinHelloUnlockExt.LockAfterAutoType = checkBox1.Checked; if (checkBox1.Checked) { WinHelloUnlockExt.database.CustomData.Set(WinHelloUnlockExt.ProductName + "AT", "true"); } else { WinHelloUnlockExt.database.CustomData.Set(WinHelloUnlockExt.ProductName + "AT", "false"); } if (onOpenEnabled != checkBox.Checked.ToString()) { if (checkBox.Checked) { WinHelloUnlockExt.database.CustomData.Set(WinHelloUnlockExt.ProductName, "true"); WinHelloUnlockExt.enablePlugin = true; } else { if (deleteButton.Enabled) { UWPLibrary.DeleteHelloData(WinHelloUnlockExt.dbName); } WinHelloUnlockExt.database.CustomData.Set(WinHelloUnlockExt.ProductName, "false"); WinHelloUnlockExt.enablePlugin = false; } } WinHelloUnlockExt.database.Modified = true; try { WinHelloUnlockExt.database.Save(null); } catch { } //WinHelloUnlockExt.database.Save(null); } }; } }
/// <summary> /// Creates the data for WinHelloUnlock to work. /// 1. A Key Credential to sign a cryptographic key. /// 2. A Password vault to save the data into /// 3. A Password Credential in which to save the encrypted data (using the signed cryptographic key). /// </summary> /// <param name="dbPath">Database path. This is the identity of the database, if Database is moved or renamed, /// WinHelloUnlock will not work and new data needs to be created. /// </param> /// <returns>True if all the data was saved successfully.</returns> internal static async Task <bool> CreateHelloData(string dbPath) { bool isHelloAvailable = await UWPLibrary.IsHelloAvailable(); if (isHelloAvailable) { KeyCredentialCreationOption optionNew = KeyCredentialCreationOption.ReplaceExisting; KeyCredentialRetrievalResult retrievalResult = await UWPLibrary.CreateCredential(dbPath, optionNew); if (retrievalResult.Status == KeyCredentialStatus.Success) { KeyList keyList = Library.GetKeys(WinHelloUnlockExt.database); string resultSave = await UWPLibrary.SaveKeys(dbPath, keyList, retrievalResult); if (resultSave == "Success") { MessageService.ShowInfo("Database Keys saved successfuly"); UWPLibrary.ck = KeePass.Program.MainForm.ActiveDatabase.MasterKey; return(true); } else { if (resultSave.Substring(0, 20) == "Value cannot be null") { MessageService.ShowWarning("Error saving the composite key: MasterKey was null." + " Verify that \"Remember master password (in encrypted form) of a database while it is open\" option under Tools/Options/Security" + " is enabled, then lock and unlock the database."); } else { MessageService.ShowWarning("Error saving the composite key: " + resultSave); } } } else { WinHelloErrors(retrievalResult.Status, "Error creating the credential: "); } } else { MessageService.ShowWarning("Windows Hello is NOT Available"); } return(false); }
/// <summary> /// Reopens the KeyPromptForm whenever a change in MasterKeyOnSecureDesktop is made. /// </summary> internal static void ReopenKeyPromptForm(KeyPromptForm keyPromptForm) { if (WinHelloUnlockExt.secureChaged) { KeePass.Program.Config.Security.MasterKeyOnSecureDesktop = true; WinHelloUnlockExt.secureChaged = false; WinHelloUnlockExt.isMonitoring = true; var _ = Task.Factory.StartNew(() => Library.CloseWarning()); UWPLibrary.Unlock(keyPromptForm, new CompositeKey()); } else { keyPromptForm.Visible = true; keyPromptForm.Opacity = 1; } }
private void RefreshOptions() { string text = ""; string path = WinHelloUnlockExt.database.IOConnectionInfo.Path; string fileName = Path.GetFileName(path); if (path.Length > 50) { text = path.Substring(0, Math.Max(0, 45 - fileName.Length)) + " ... " + fileName; } else { text = path; } settingsGroupBox.Text = "WinHelloUnlock Settings for " + text; first = Task.Run(() => UWPLibrary.FirstTime(db)).Result; onOpenEnabled = WinHelloUnlockExt.database.CustomData.Get(WinHelloUnlockExt.ProductName); enabled = true; if (onOpenEnabled == "false") { enabled = false; } if (enabled && !first) { infoLabel.Text = "WinHelloUnlock is configured for this Database"; } else { infoLabel.Text = "WinHelloUnlock is NOT configured for this Database"; } //checkBox.Checked = enabled; createButton.Enabled = first && (enabled || checkBox.Checked); deleteButton.Enabled = !first; //checkBox1.Checked = WinHelloUnlockExt.LockAfterAutoType; checkBox1.Enabled = enabled; }
/// <summary> /// Creates the data for WinHelloUnlock to work. /// 1. A Key Credential to sign a cryptographic key. /// 2. A Password vault to save the data into /// 3. A Password Credential in which to save the encrypted data (using the signed cryptographic key). /// </summary> /// <param name="dbPath">Database path. This is the identity of the database, if Database is moved or renamed, /// WinHelloUnlock will not work and new data needs to be created. /// </param> /// <returns>True if all the data was saved successfully.</returns> internal static async Task <bool> CreateHelloData(string dbPath) { bool isHelloAvailable = await UWPLibrary.IsHelloAvailable(); if (isHelloAvailable) { KeyCredentialCreationOption optionNew = KeyCredentialCreationOption.ReplaceExisting; KeyCredentialRetrievalResult retrievalResult = await UWPLibrary.CreateCredential(dbPath, optionNew); if (retrievalResult.Status == KeyCredentialStatus.Success) { KeyList keyList = Library.GetKeys(WinHelloUnlockExt.database); string resultSave = await UWPLibrary.SaveKeys(dbPath, keyList, retrievalResult); if (resultSave == "Success") { MessageService.ShowInfo("Database Keys saved successfuly"); UWPLibrary.ck = KeePass.Program.MainForm.ActiveDatabase.MasterKey; return(true); } else { MessageService.ShowWarning("Error saving the composite key: " + resultSave); } } else { WinHelloErrors(retrievalResult.Status, "Error creating the credential: "); } } else { MessageService.ShowWarning("Windows Hello is NOT Available"); } return(false); }
/// <summary> /// When the plugin detects a change in the MasterKey, it uses this method to prompt the user to update or delete the data /// </summary> /// <param name="ioInfo">IOConnectionInfo that represents the database.</param> /// <param name="dbPath">Name of the credential to update or delete.</param> /// <param name="opening">If the masterkey change was detected during database unlock use true.</param> internal static async Task HandleMasterKeyChange(IOConnectionInfo ioInfo, string dbPath, bool opening) { if (opening) { string str = WinHelloUnlockExt.ProductName + " could not unlock this database." + " MasterKey must have changed. Delete " + WinHelloUnlockExt.ProductName + " data?"; if (MessageService.AskYesNo(str, WinHelloUnlockExt.ProductName)) { UWPLibrary.DeleteHelloData(dbPath); } WinHelloUnlockExt.opened = true; WinHelloUnlockExt.Host.MainWindow.OpenDatabase(ioInfo, null, false); } else { string str = "A change in MasterKey has been detected. Do you want to update " + WinHelloUnlockExt.ProductName + " data?"; if (MessageService.AskYesNo(str, WinHelloUnlockExt.ProductName)) { UWPLibrary.DeleteHelloData(dbPath); await UWPLibrary.CreateHelloData(dbPath); } } }
/// <summary> /// Used to modify other form when they load. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void WindowAddedHandler(object sender, GwmWindowEventArgs e) { // If a database is attempted to be unlocked if (e.Form is KeyPromptForm keyPromptForm) { keyPromptForm.Opacity = 0; keyPromptForm.Visible = false; var mf = KeePass.Program.MainForm; isAutoTyping = (bool)mf.GetType().GetField("m_bIsAutoTyping", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(mf); var fieldInfo = keyPromptForm.GetType().GetField("m_ioInfo", BindingFlags.Instance | BindingFlags.NonPublic); var ioInfo = fieldInfo.GetValue(keyPromptForm) as IOConnectionInfo; string dbName = Library.CharChange(ioInfo.Path); bool isHelloAvailable = await UWPLibrary.IsHelloAvailable(); // if the database has credentials saved and Windows Hello is available if (!await UWPLibrary.FirstTime(dbName) && isHelloAvailable) { // If there is no other Windows Hello Prompt opened if (opened) { opened = false; Library.UnlockDatabase(ioInfo, keyPromptForm); } else // If there is another Windows Hello Prompt opened, just close this regular prompt // This is usefull for when there is a double attempt to unlock the database by some plugins (ChromeIPass) { Library.CloseFormWithResult(keyPromptForm, DialogResult.Cancel); } } else if (!await UWPLibrary.FirstTime(dbName)) { MessageService.ShowInfo("This Database has credential data saved. Enable Windows Hello to use."); keyPromptForm.Opacity = 1; keyPromptForm.Visible = true; } else { keyPromptForm.Opacity = 1; keyPromptForm.Visible = true; } } // If the Options window is opened if (e.Form is OptionsForm optionsForm) { if (!host.MainWindow.ActiveDatabase.IsOpen) { return; // If there is no database opened, don't do anything } optionsForm.Shown += (object sender2, EventArgs e2) => { try { Library.AddWinHelloOptions(optionsForm); } catch (Exception ex) { MessageService.ShowWarning("WinHelloUnlock Error: " + ex.Message); } }; } // If the Update Check Window is opened. // This is used because the Update Check Window prevents the a database from being opened if (e.Form is UpdateCheckForm ucf && !opened) { WinHelloUnlockExt.updateCheckForm = ucf; } }
/// <summary>Deletes WinHelloUnock data when the Delete Button is clicked.</summary> private void DeleteteButton_Click(object sender, EventArgs e) { UWPLibrary.DeleteHelloData(WinHelloUnlockExt.dbName); RefreshOptions(); }
/// <summary>Creates WinHelloUnock data when the Create Button is clicked.</summary> private async void CreateButton_Click(object sender, EventArgs e) { await UWPLibrary.CreateHelloData(WinHelloUnlockExt.dbName); RefreshOptions(); }