private bool SearchHibpForPasswords() { var sw = Stopwatch.StartNew(); var sha1 = new SHA1Managed(); var userEntryDictionary = new Dictionary <string, List <Entry> >(); foreach (var entry in this.Document.Entries) { if (entry.Records.Contains(RecordType.Password)) { foreach (var record in entry.Records) { if (record.RecordType == RecordType.Password) { var sha1Bytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(record.Text)); var passwordHash = BitConverter.ToString(sha1Bytes).Replace("-", ""); //dirty conversion to hex if (!string.IsNullOrWhiteSpace(passwordHash)) { if (!userEntryDictionary.TryGetValue(passwordHash, out var entries)) { entries = new List <Entry>(); userEntryDictionary.Add(passwordHash, entries); } if (!entries.Contains(entry)) { entries.Add(entry); } } } } } if (bwSearchHibp.CancellationPending) { return(false); } } Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Passwords hashed at {0:0.0} ms", sw.ElapsedMilliseconds)); //sort all passwords var userPasswords = new List <PasswordAndEntryStorage>(); foreach (var kvp in userEntryDictionary) { userPasswords.Add(new PasswordAndEntryStorage(kvp.Key, kvp.Value)); if (bwSearchHibp.CancellationPending) { return(false); } } Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Passwords filled at {0:0.0} ms", sw.ElapsedMilliseconds)); //sort based on random value if (Settings.HibpCheckWeakPasswordInRandomOrder) { userPasswords.Sort((item1, item2) => { var count1 = item1.Entries.Count; var count2 = item2.Entries.Count; if (count1 > count2) //sort based on number of entries with the same password { return(-1); } else if (count1 < count2) { return(+1); } else //just randomize { return((item1.RandomValue < item2.RandomValue) ? -1 : (item1.RandomValue > item2.RandomValue) ? +1 : 0); } }); } else { userPasswords.Sort((item1, item2) => { var count1 = item1.Entries.Count; var count2 = item2.Entries.Count; if (count1 > count2) //sort based on number of entries with the same password { return(-1); } else if (count1 < count2) { return(+1); } else //order by password hash { return(string.CompareOrdinal(item1.PasswordHash, item2.PasswordHash)); } }); } Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Passwords sorted at {0:0.0} ms", sw.ElapsedMilliseconds)); //check all hashes var userPasswordCount = userPasswords.Count; for (var i = 0; i < userPasswordCount; i++) { var percentage = i * 100 / userPasswordCount; var item = userPasswords[i]; try { if (Hibp.IsPassworPwned(item.PasswordHash)) { ReportWebStatus(percentage, HttpStatusCode.OK, null, item.Entries[0].Title, (userPasswordCount - i) * Settings.HibpThrottleInterval); foreach (var entry in item.Entries) { var lvi = new ListViewItem(entry.Title) { Tag = entry, ImageIndex = 1, ToolTipText = $"Password is present in breached password collection at Have I been pwned? site." }; bwSearchHibp.ReportProgress(percentage, new ProgressState(lvi)); } } else { ReportWebStatus(percentage, HttpStatusCode.NotFound, null, item.Entries[0].Title, (userPasswordCount - i) * Settings.HibpThrottleInterval); } } catch (WebException ex) { if (ex.Response is HttpWebResponse response) { ReportWebStatus(percentage, response.StatusCode, response.StatusDescription, item.Entries[0].Title, (userPasswordCount - i) * Settings.HibpThrottleInterval); } else { ReportWebStatus(percentage, null, ex.Message, item.Entries[0].Title, (userPasswordCount - i) * Settings.HibpThrottleInterval); } } Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Password for {1} entries searched at {0:0.0} ms (followed by {2} ms throttling)", sw.ElapsedMilliseconds, item.Entries.Count, Settings.HibpThrottleInterval)); Thread.Sleep(Settings.HibpThrottleInterval); } Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "{1} accounts searched in {0:0.0} ms (albeit with {2} ms throttling)", sw.ElapsedMilliseconds, userPasswords.Count, Settings.HibpThrottleInterval)); return(true); }
private bool SearchHibpForBreaches() { //collect all user-names var sw = Stopwatch.StartNew(); var userEntryDictionary = new Dictionary <string, List <Entry> >(); foreach (var entry in this.Document.Entries) { if (entry.Records.Contains(RecordType.Password)) { foreach (var record in entry.Records) { if ((record.RecordType == RecordType.EmailAddress) || (record.RecordType == RecordType.UserName)) { var text = record.Text; if (!string.IsNullOrWhiteSpace(text)) { if (!userEntryDictionary.TryGetValue(text, out var entries)) { entries = new List <Entry>(); userEntryDictionary.Add(text, entries); } if (!entries.Contains(entry)) { entries.Add(entry); } } } } } if (bwSearchHibp.CancellationPending) { return(false); } } Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "User names cached at {0:0.0} ms", sw.ElapsedMilliseconds)); //move dictionary to list so it gets sorted based on frequency var userEntryList = new List <AccountAndEntryStorage>(); foreach (var kvp in userEntryDictionary) { userEntryList.Add(new AccountAndEntryStorage(kvp.Key, kvp.Value)); } userEntryList.Sort((item1, item2) => { var count1 = item1.Entries.Count; var count2 = item2.Entries.Count; return((count1 > count2) ? -1 : (count1 < count2) ? +1 : string.CompareOrdinal(item1.Account, item2.Account)); }); Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "User names sorted at {0:0.0} ms", sw.ElapsedMilliseconds)); //Search all accounts for breaches var userEntryCount = userEntryList.Count; for (var i = 0; i < userEntryCount; i++) { var userEntry = userEntryList[i]; var percentage = i * 100 / userEntryCount; try { var breaches = Hibp.GetAllBreaches(userEntry.Account); ReportWebStatus(percentage, HttpStatusCode.OK, null, userEntry.Account, (userEntryCount - i) * Settings.HibpThrottleInterval); foreach (var entry in userEntry.Entries) { var modified = entry.PasswordModificationTime; foreach (var record in entry.Records) { if (record.RecordType == RecordType.Url) { var url = record.Text; foreach (var breach in breaches) { if (breach.IsApplicable(url, modified, entry.Title)) //check only if we know the domain { var lvi = new ListViewItem(entry.Title) { Tag = entry, ImageIndex = 1, ToolTipText = $"Account is present in {breach.Title} breach:\n{FilterHtml(breach.Description)}" }; bwSearchHibp.ReportProgress(percentage, new ProgressState(lvi)); break; } } } } } } catch (WebException ex) { if (ex.Response is HttpWebResponse response) { ReportWebStatus(percentage, response.StatusCode, response.StatusDescription, userEntry.Account, (userEntryCount - i) * Settings.HibpThrottleInterval); } else { ReportWebStatus(percentage, null, ex.Message, userEntry.Account, (userEntryCount - i) * Settings.HibpThrottleInterval); } } Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Account {1} searched at {0:0.0} ms (followed by {2} ms throttling)", sw.ElapsedMilliseconds, userEntry.Account, Settings.HibpThrottleInterval)); Thread.Sleep(Settings.HibpThrottleInterval); } Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "{1} accounts searched in {0:0.0} ms (albeit with {2} ms throttling)", sw.ElapsedMilliseconds, userEntryList.Count, Settings.HibpThrottleInterval)); return(true); }