From 51e5abd846ff435d97d8bbe521a036d36ba9602a Mon Sep 17 00:00:00 2001 From: Mihai Ciuraru Date: Tue, 22 Jan 2019 01:11:40 +0200 Subject: [PATCH] Support for password references --- HIBPOfflineCheckExt.cs | 118 +++++++++++++++++-------------------- Properties/AssemblyInfo.cs | 4 +- readme.md | 10 ++-- version.txt | 2 +- 4 files changed, 62 insertions(+), 72 deletions(-) diff --git a/HIBPOfflineCheckExt.cs b/HIBPOfflineCheckExt.cs index de12dbf..81e1514 100644 --- a/HIBPOfflineCheckExt.cs +++ b/HIBPOfflineCheckExt.cs @@ -12,7 +12,6 @@ using System.Diagnostics; using KeePassLib.Utility; using KeePass.Util; -using System.Diagnostics.CodeAnalysis; using System.Text; using KeePass.Util.Spr; @@ -37,13 +36,6 @@ public override bool Initialize(IPluginHost host) if (host == null) return false; PluginHost = host; - - ToolStripItemCollection tsMenu = PluginHost.MainWindow.ToolsMenu.DropDownItems; - tsMenu.Add(new ToolStripSeparator()); - ToolStripMenuItem tsMenuItem = new ToolStripMenuItem("HIBP Offline Check..."); - tsMenuItem.Click += new EventHandler(ToolsMenuItemClick); - tsMenu.Add(tsMenuItem); - prov = new HIBPOfflineColumnProv() { Host = host }; options = LoadOptions(); @@ -54,6 +46,18 @@ public override bool Initialize(IPluginHost host) return true; } + public override ToolStripMenuItem GetMenuItem(PluginMenuType t) + { + if (t == PluginMenuType.Main) + { + ToolStripMenuItem tsMenuItem = new ToolStripMenuItem("HIBP Offline Check..."); + tsMenuItem.Click += new EventHandler(ToolsMenuItemClick); + return tsMenuItem; + } + + return null; + } + private void ToolsMenuItemClick(object sender, EventArgs e) { HIBPOfflineCheckOptions optionsForm = new HIBPOfflineCheckOptions(this); @@ -183,14 +187,14 @@ public override bool SupportsCellAction(string strColumnName) return (strColumnName == PluginOptions.ColumnName); } - [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Dispose is idempotent")] private void GetPasswordStatus() { var pwd_sha_str = String.Empty; using (var sha1 = new SHA1CryptoServiceProvider()) { - var password = SprEngine.Compile(PasswordEntry.Strings.GetSafe(PwDefs.PasswordField).ReadString(), new SprContext(PasswordEntry,Host.Database, SprCompileFlags.All)); + var context = new SprContext(PasswordEntry, Host.Database, SprCompileFlags.All); + var password = SprEngine.Compile(PasswordEntry.Strings.GetSafe(PwDefs.PasswordField).ReadString(), context); var pwd_sha_bytes = sha1.ComputeHash(UTF8Encoding.UTF8.GetBytes(password)); var sb = new StringBuilder(2 * pwd_sha_bytes.Length); @@ -210,75 +214,61 @@ private void GetPasswordStatus() return; } - FileStream fs = null; - StreamReader sr = null; - - try + using (FileStream fs = File.OpenRead(latestFile)) + using (StreamReader sr = new StreamReader(fs)) { - fs = File.OpenRead(latestFile); - sr = new StreamReader(fs); - - string line; - Status = PluginOptions.SecureText; - int sha_len = pwd_sha_str.Length; - - var low = 0L; - var high = fs.Length; - - while (low <= high) + try { - var middle = (low + high + 1) / 2; - fs.Seek(middle, SeekOrigin.Begin); + string line; + Status = PluginOptions.SecureText; + int sha_len = pwd_sha_str.Length; - // Resync with base stream after seek - sr.DiscardBufferedData(); + var low = 0L; + var high = fs.Length; - line = sr.ReadLine(); + while (low <= high) + { + var middle = (low + high + 1) / 2; + fs.Seek(middle, SeekOrigin.Begin); - if (sr.EndOfStream) break; + // Resync with base stream after seek + sr.DiscardBufferedData(); - // We may have read only a partial line so read again to make sure we get a full line - if (middle > 0) line = sr.ReadLine() ?? String.Empty; + line = sr.ReadLine(); - int compare = String.Compare(pwd_sha_str, line.Substring(0, sha_len), StringComparison.Ordinal); + if (sr.EndOfStream) break; - if (compare < 0) - { - high = middle - 1; - } - else if (compare > 0) - { - low = middle + 1; - } - else - { - string[] tokens = line.Split(':'); - Status = PluginOptions.InsecureText; - insecureWarning = true; + // We may have read only a partial line so read again to make sure we get a full line + if (middle > 0) line = sr.ReadLine() ?? String.Empty; - if (PluginOptions.BreachCountDetails) + int compare = String.Compare(pwd_sha_str, line.Substring(0, sha_len), StringComparison.Ordinal); + + if (compare < 0) + { + high = middle - 1; + } + else if (compare > 0) { - Status += " (password count: " + tokens[1].Trim() + ")"; + low = middle + 1; } + else + { + string[] tokens = line.Split(':'); + Status = PluginOptions.InsecureText; + insecureWarning = true; - break; + if (PluginOptions.BreachCountDetails) + { + Status += " (password count: " + tokens[1].Trim() + ")"; + } + + break; + } } } - } - catch - { - Status = "Failed to read HIBP file"; - } - finally - { - if (sr != null) - { - sr.Dispose(); - } - - if (fs != null) + catch { - fs.Dispose(); + Status = "Failed to read HIBP file"; } } } diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 1fb4264..b8946d4 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.1.0")] -[assembly: AssemblyFileVersion("1.3.1.0")] +[assembly: AssemblyVersion("1.3.2.0")] +[assembly: AssemblyFileVersion("1.3.2.0")] diff --git a/readme.md b/readme.md index 64e2ae9..c6bdc58 100644 --- a/readme.md +++ b/readme.md @@ -13,26 +13,26 @@ for all selected passwords. While it does provide an API for securely checking the passwords online, some bits of a hashed password still need to be sent to the service when performing this type of check. -This plugin offers the alternative of an offline check, by using the 30GB file provided by [Have I been pwned?](https://haveibeenpwned.com/). +This plugin offers the alternative of an offline check, by using the downloadable file provided by [Have I been pwned?](https://haveibeenpwned.com/). The plugin adds a new column to KeePass. When double-clicking the column for a specific entry, the sha1 hash is calculated for the password, which is then searched in the file. A status will be displayed on the column for that specific password. ## Features -- binary search in the 30GB file gives an instant result +- binary search in the large password file gives an instant result - the status (Pwned or Secure) is saved in the KeePass database and will be retrieved when reopening the app, and updated if the password entry changes - each password is individually checked only on user request - multiple passwords can be checked in bulk by using the right click menu (under "Selected Entries") ## Prerequisites -- Download the [pwned-passwords-ordered-by-hash.txt](https://downloads.pwnedpasswords.com/passwords/pwned-passwords-ordered-by-hash.7z.torrent) file from +- Download the [pwned-passwords-sha1-ordered-by-hash-v4.txt](https://haveibeenpwned.com/Passwords) file from haveibeenpwned.com [password list](https://haveibeenpwned.com/Passwords). Use the torrent if possible, as suggested by the author. - __It's important that you get the -ordered-by-hash- version of the file, the plugin uses it for fast searching__. + __It's important that you get the SHA-1 (ordered by hash) version of the file, the plugin uses it for fast searching__. - Extract the file from the 7zip archive -- Place the `pwned-passwords-ordered-by-hash.txt` in the same location as `KeePass.exe` +- Place the `pwned-passwords-sha1-ordered-by-hash-v4.txt` file in the same location as `KeePass.exe` (file location is configurable in the options) ## Installation diff --git a/version.txt b/version.txt index f78e561..c6cfe76 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ : -HIBPOfflineCheck:1.3.1.0 +HIBPOfflineCheck:1.3.2.0 :