/// <summary> /// Saves an encrypted string containing the CompositeKey information to the Password vault. /// </summary> /// <param name="dbPath">Database Path. This is the identification of the database, if database is moved or name is changed, /// New credentials must be created.</param> /// <param name="keyList">KeyList object containing the composite key information.</param> /// <param name="rResult">KeyCredential object used to sign a key to encrypt the compositekey information.</param> /// <returns>String representing the result of the operation. Success or the error thrown.</returns> internal static async Task <string> SaveKeys(string dbPath, KeyList keyList, KeyCredentialRetrievalResult rResult) { try { PasswordVault myVault = new PasswordVault(); String encrypted = await Encrypt(Library.ConvertToPString(keyList), rResult); PasswordCredential newCredential = new PasswordCredential(dbPath, WinHelloUnlockExt.ProductName, encrypted); newCredential.RetrievePassword(); myVault.Add(newCredential); return("Success"); } catch (Exception ev) { return(ev.Message); } }
/// <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> /// Converts a KeyList object to a properly formatted ProtectedString /// </summary> /// <param name="keys">KeyList containing the composite key information.</param> /// <returns>ProtectedString containing KeyList information.</returns> internal static ProtectedString ConvertToPString(KeyList keys) { string div2 = WinHelloUnlockExt.ProductName + ","; string div = WinHelloUnlockExt.ShortProductName + ","; if (keys.Pass == null || keys.KeyName == null || keys == null) { return(null); } ProtectedString pass = ProtectedString.EmptyEx; foreach (ProtectedString ps in keys.Pass) { pass += ps + div; } pass = pass.Remove(pass.Length - div.Length, div.Length); ProtectedString key = new ProtectedString(true, string.Join(div, keys.KeyName)); key = key.Insert(key.Length, div2); return(key + pass); }
/// <summary> /// Retrieves the CompositeKey information from the Password vault. /// </summary> /// <param name="dbPath">Database Path. This is the identification of the database, if database is moved or name is changed, /// New credentials must be created.</param> /// <param name="rResult">KeyCredential object used to sign a key to decrypt the compositekey information.</param> /// <returns>KeyList object with all the information to compose the CompositeKey.</returns> internal async static Task <KeyList> RetrieveKeys(string dbPath, KeyCredentialRetrievalResult rResult) { PasswordVault myVault = new PasswordVault(); var newCredential = new PasswordCredential(); try { newCredential = myVault.Retrieve(dbPath, WinHelloUnlockExt.ProductName); newCredential.RetrievePassword(); } catch (Exception ev) { MessageService.ShowInfo("Not able to retrieve Composite Key from Microsoft Password Vault. " + "Maybe saving again will solve the problem. Message: " + ev.Message); return(new KeyList(null, null)); } try { string encrypted = newCredential.Password; ProtectedString decrypted = await Decrypt(encrypted, rResult); if (decrypted != null) { KeyList Keys = Library.ConvertKeyList(decrypted); decrypted = ProtectedString.EmptyEx; return(Keys); } else { return(new KeyList(null, null)); } } catch (Exception ev) { MessageService.ShowInfo("Credential not retrieved: " + ev.Message); Debug.Write(ev.Message); return(new KeyList(null, 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 { 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> /// Performs the actual unlock of the database /// </summary> /// <param name="ioInfo">IOConnectionInfo that represents the Database.</param> internal static async Task UnlockDatabase(IOConnectionInfo ioInfo, KeyPromptForm keyPromptForm) { string dbPath = Library.CharChange(ioInfo.Path); KeyCredentialRetrievalResult retrievalResult = await OpenCredential(dbPath); if (retrievalResult.Status == KeyCredentialStatus.Success) // If credential is successfully retrieved { KeyList keyList = await RetrieveKeys(dbPath, retrievalResult); CompositeKey compositeKey = Library.ConvertToComposite(keyList); // Composite Key is retrieved so the number of tries is augmented ++WinHelloUnlockExt.tries; if (compositeKey != null) // If there is actually a composite key { if (Library.CheckMasterKey(ioInfo, compositeKey)) // If the composite key actually unlocks the database { // Unlock the database Unlock(keyPromptForm, compositeKey); // Check if the database was actually opened string openedDBPath = WinHelloUnlockExt.Host.MainWindow.ActiveDatabase.IOConnectionInfo.Path; if (openedDBPath != ioInfo.Path && // If opened DB is not the same we are trying to open WinHelloUnlockExt.updateCheckForm != null) // and UpdateCheckForm is opened then the database was not opened because of that { // Using a global Composite key to test for changes ck = compositeKey; // Register an event handler to be able to unlock the database after updateCheckForm is closed WinHelloUnlockExt.updateCheckForm.FormClosed += (object sender, FormClosedEventArgs e) => UpdateFormClosedEventHandler(ioInfo); } if (WinHelloUnlockExt.isAutoTyping && WinHelloUnlockExt.LockAfterAutoType) { var _ = Task.Factory.StartNew(() => LockAfterAutoType()); //MessageService.ShowInfo("autotyping"); } } else // If composite key did not unlock the database prompt the user to delete the credentials { await Library.HandleMasterKeyChange(ioInfo, dbPath, true); } } else // If compositeKey is null, open regular unlock prompt to unlock the database { if (WinHelloUnlockExt.secureChaged) { KeePass.Program.Config.Security.MasterKeyOnSecureDesktop = true; WinHelloUnlockExt.secureChaged = false; Library.CloseFormWithResult(keyPromptForm, DialogResult.Cancel); var _ = Task.Factory.StartNew(() => KeePass.Program.MainForm.OpenDatabase(ioInfo, null, true)); } else { keyPromptForm.Visible = true; keyPromptForm.Opacity = 1; } } // Delete composite key data compositeKey = null; // Delete KeyList object keyList = new KeyList(null, null); } else { // If credentials were not successfully retrieved inform the user and open regular unlock prompt WinHelloErrors(retrievalResult.Status, "Error unlocking database: "); if (WinHelloUnlockExt.secureChaged) { KeePass.Program.Config.Security.MasterKeyOnSecureDesktop = true; WinHelloUnlockExt.secureChaged = false; Library.CloseFormWithResult(keyPromptForm, DialogResult.Cancel); var _ = Task.Factory.StartNew(() => KeePass.Program.MainForm.OpenDatabase(ioInfo, null, true)); } else { keyPromptForm.Visible = true; keyPromptForm.Opacity = 1; } } }