diff --git a/LICENSE b/LICENSE
index 2bb439f..de8d2ed 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,5 @@
Copyright (c) 2012 Dropbox, Inc.
+Copyright (c) 2020 Tony Richards
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/README.md b/README.md
index f675ef5..b5c302f 100644
--- a/README.md
+++ b/README.md
@@ -25,69 +25,36 @@ From the `Zxcvbn` readme:
>
> http://tech.dropbox.com/?p=165
-This port aims to produce comparable results with the JS version of `Zxcvbn`. The results
-structure that is returned can be interpreted in the same way as with JS `Zxcvbn` and this
-port has been tested with a variety of passwords to ensure that it return the same results
-as the JS version.
-
-There are some implementation differences, however, so exact results are not guaranteed.
+This port aims to produce comparable results with the Typescript version of `Zxcvbn` which I have also put out and is here https://github.com/trichards57/zxcvbn.
+The results structure that is returned can be interpreted in the same way as with JS `Zxcvbn` and this port has been tested with a variety of passwords to ensure
+that it return the same score as the JS version (some other details vary a little).
+I have tried to keep the implementation as close as possible, but there is still a chance of some small changes. Let me know if you find any differences
+and I can investigate.
### Using `Zxcvbn-cs`
The included Visual Studio project will create a single assembly, Zxcvbn.dll, which is all that is
required to be included in your project.
-To evaluate a single password:
-
-``` C#
-using Zxcvbn;
-
-//...
-
-var result = Zxcvbn.MatchPassword("p@ssw0rd");
-```
-
-To evaluate many passwords, create an instance of `Zxcvbn` and then use that to evaluate your passwords.
-This avoids reloading dictionaries etc. for every password:
+To evaluate a password:
``` C#
using Zxcvbn;
//...
-var zx = new Zxcvbn();
-
-foreach (var password in passwords)
-{
- var result = zx.EvaluatePassword(password);
-
- //...
-}
+var result = Zxcvbn.Core.EvaluatePassword("p@ssw0rd");
```
-Both `MatchPassword` and `EvaluatePassword` take an optional second parameter that contains an enumerable of
+`EvaluatePassword` takes an optional second parameter that contains an enumerable of
user data strings to also match the password against.
### Interpreting Results
-The `Result` structure returned from password evaluation is interpreted the same way as with JS `Zxcvbn`:
-
-- `result.Entropy`: bits of entropy for the password
-- `result.CrackTime`: an estimation of actual crack time, in seconds.
-- `result.CrackTimeDisplay`: the crack time, as a friendlier string: "instant", "6 minutes", "centuries", etc.
-- `result.Score`: [0,1,2,3,4] if crack time is less than [10\*\*2, 10\*\*4, 10\*\*6, 10\*\*8, Infinity]. (useful for implementing a strength bar.)
-- `result.MatchSequence`: the list of pattern matches that was used to calculate Entropy.
-- `result.CalculationTime`: how long `Zxcvbn` took to calculate the results.
-
-### More Information
-
-For more information on why password entropy is calculated as it is, refer to `Zxcvbn`s originators:
-
-https://github.com/lowe/zxcvbn
-
-http://tech.dropbox.com/?p=165
+The `Result` structure returned from password evaluation is interpreted the same way as with JS `Zxcvbn`.
+- `result.Score`: 0-4 indicating the estimated strength of the password.
### Licence
diff --git a/appveyor.yml b/appveyor.yml
index 5ba91de..e241549 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 2.0.{build}
+version: 5.0.{build}
image: Visual Studio 2019
init:
- git config --global core.autocrlf true
diff --git a/zxcvbn-core-test-console/Program.cs b/zxcvbn-core-test-console/Program.cs
new file mode 100644
index 0000000..f7c9b69
--- /dev/null
+++ b/zxcvbn-core-test-console/Program.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Zxcvbn.TestConsole
+{
+ internal class Program
+ {
+ private static void Main(string[] args)
+ {
+ var result = Zxcvbn.Core.EvaluatePassword("Applesoranges!");
+
+ Console.WriteLine(result.Score);
+ }
+ }
+}
diff --git a/zxcvbn-core-test-console/zxcvbn-core-test-console.csproj b/zxcvbn-core-test-console/zxcvbn-core-test-console.csproj
new file mode 100644
index 0000000..b484432
--- /dev/null
+++ b/zxcvbn-core-test-console/zxcvbn-core-test-console.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ netcoreapp3.1
+ Zxcvbn.TestConsole
+ zxcvbn-core-test-console
+ false
+
+
+
+
+
+
diff --git a/zxcvbn-core/Feedback.cs b/zxcvbn-core/Feedback.cs
index ae0719f..9711427 100644
--- a/zxcvbn-core/Feedback.cs
+++ b/zxcvbn-core/Feedback.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using Zxcvbn.Matcher.Matches;
@@ -93,7 +92,7 @@ private static FeedbackItem GetDictionaryMatchFeedback(DictionaryMatch match, bo
var word = match.Token;
if (char.IsUpper(word[0]))
suggestions.Add("Capitalization doesn't help very much");
- else if (word.All(c => char.IsUpper(c)) && word.ToLower(CultureInfo.InvariantCulture) != word)
+ else if (word.All(c => char.IsUpper(c)) && word.ToLower() != word)
suggestions.Add("All-uppercase is almost as easy to guess as all-lowercase");
if (match.Reversed && match.Token.Length >= 4)
diff --git a/zxcvbn-core/GlobalSuppressions.cs b/zxcvbn-core/GlobalSuppressions.cs
index 0e74275..c9c52ea 100644
--- a/zxcvbn-core/GlobalSuppressions.cs
+++ b/zxcvbn-core/GlobalSuppressions.cs
@@ -10,3 +10,5 @@
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "To match this project's coding style.")]
[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "This normalization isn't security critical")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "No header required.")]
+[assembly: SuppressMessage("Globalization", "CA1304:Specify CultureInfo", Justification = "Not supported in all the desired target libraries.")]
+[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "Not supported in all the desired target libraries.")]
diff --git a/zxcvbn-core/Matcher/DictionaryMatcher.cs b/zxcvbn-core/Matcher/DictionaryMatcher.cs
index c10e8d8..94802c9 100644
--- a/zxcvbn-core/Matcher/DictionaryMatcher.cs
+++ b/zxcvbn-core/Matcher/DictionaryMatcher.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
using Zxcvbn.Matcher.Matches;
@@ -42,7 +41,7 @@ public DictionaryMatcher(string name, IEnumerable wordList)
dictionaryName = name;
// Must ensure that the dictionary is using lowercase words only
- rankedDictionary = BuildRankedDictionary(wordList.Select(w => w.ToLower(CultureInfo.InvariantCulture)));
+ rankedDictionary = BuildRankedDictionary(wordList.Select(w => w.ToLower()));
}
///
@@ -52,7 +51,7 @@ public DictionaryMatcher(string name, IEnumerable wordList)
/// An enumerable of dictionary matches.
public virtual IEnumerable MatchPassword(string password)
{
- var passwordLower = password.ToLower(CultureInfo.InvariantCulture);
+ var passwordLower = password.ToLower();
var length = passwordLower.Length;
var matches = new List();
diff --git a/zxcvbn-core/Matcher/L33tMatcher.cs b/zxcvbn-core/Matcher/L33tMatcher.cs
index 08e8533..512b33a 100644
--- a/zxcvbn-core/Matcher/L33tMatcher.cs
+++ b/zxcvbn-core/Matcher/L33tMatcher.cs
@@ -73,7 +73,7 @@ public IEnumerable MatchPassword(string password)
foreach (DictionaryMatch match in matcher.MatchPassword(subbedPassword))
{
var token = password.Substring(match.i, match.j - match.i + 1);
- if (token.Equals(match.MatchedWord, StringComparison.InvariantCultureIgnoreCase))
+ if (token.ToLower().Equals(match.MatchedWord.ToLower()))
continue;
var matchSub = new Dictionary();
diff --git a/zxcvbn-core/Result.cs b/zxcvbn-core/Result.cs
index 8da4fbf..72bcadb 100644
--- a/zxcvbn-core/Result.cs
+++ b/zxcvbn-core/Result.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Zxcvbn.Matcher.Matches;
namespace Zxcvbn
@@ -31,7 +32,12 @@ public class Result
///
/// Gets the number of guesses the password is estimated to need.
///
- public double Guesses { get; internal set; }
+ public long Guesses { get; internal set; }
+
+ ///
+ /// Gets log10(the number of guesses) the password is estimated to need.
+ ///
+ public double GuessesLog10 => Math.Log10(Guesses);
///
/// Gets the sequence of matches that were used to assess the password.
diff --git a/zxcvbn-core/Scoring/BruteForceGuessesCalculator.cs b/zxcvbn-core/Scoring/BruteForceGuessesCalculator.cs
index ad1f365..2dc5b01 100644
--- a/zxcvbn-core/Scoring/BruteForceGuessesCalculator.cs
+++ b/zxcvbn-core/Scoring/BruteForceGuessesCalculator.cs
@@ -27,13 +27,13 @@ internal class BruteForceGuessesCalculator
/// The guesses estimate.
public static long CalculateGuesses(BruteForceMatch match)
{
- var guesses = (long)Math.Pow(BruteforceCardinality, match.Token.Length);
+ var guesses = Math.Pow(BruteforceCardinality, match.Token.Length);
if (double.IsPositiveInfinity(guesses))
- guesses = long.MaxValue;
+ guesses = double.MaxValue;
var minGuesses = match.Token.Length == 1 ? MinSubmatchGuessesSingleCharacter + 1 : MinSubmatchGuessesMultiCharacter + 1;
- return Math.Max(guesses, minGuesses);
+ return (long)Math.Max(guesses, minGuesses);
}
}
}
diff --git a/zxcvbn-core/Scoring/DictionaryGuessesCalculator.cs b/zxcvbn-core/Scoring/DictionaryGuessesCalculator.cs
index fe272cd..da4fa5c 100644
--- a/zxcvbn-core/Scoring/DictionaryGuessesCalculator.cs
+++ b/zxcvbn-core/Scoring/DictionaryGuessesCalculator.cs
@@ -1,5 +1,4 @@
using System;
-using System.Globalization;
using System.Linq;
using Zxcvbn.Matcher.Matches;
@@ -40,8 +39,8 @@ internal static long L33tVariations(DictionaryMatch match)
foreach (var subbed in match.Sub.Keys)
{
var unsubbed = match.Sub[subbed];
- var s = match.Token.ToLower(CultureInfo.InvariantCulture).Count(c => c == subbed);
- var u = match.Token.ToLower(CultureInfo.InvariantCulture).Count(c => c == unsubbed);
+ var s = match.Token.ToLower().Count(c => c == subbed);
+ var u = match.Token.ToLower().Count(c => c == unsubbed);
if (s == 0 || u == 0)
{
@@ -67,7 +66,7 @@ internal static long L33tVariations(DictionaryMatch match)
/// The number of possible variations.
internal static long UppercaseVariations(string token)
{
- if (token.All(c => char.IsLower(c)) || token.ToLower(CultureInfo.InvariantCulture) == token)
+ if (token.All(c => char.IsLower(c)) || token.ToLower() == token)
return 1;
if ((char.IsUpper(token.First()) && token.Skip(1).All(c => char.IsLower(c)))
diff --git a/zxcvbn-core/TimeEstimates.cs b/zxcvbn-core/TimeEstimates.cs
index 8a2d089..c03cbba 100644
--- a/zxcvbn-core/TimeEstimates.cs
+++ b/zxcvbn-core/TimeEstimates.cs
@@ -16,14 +16,14 @@ public static AttackTimes EstimateAttackTimes(double guesses)
{
var crackTimesSeconds = new CrackTimes
{
- OfflineFastHashing1e10PerSecond = guesses / (100 / 3600),
+ OfflineFastHashing1e10PerSecond = guesses / (100.0 / 3600),
OfflineSlowHashing1e4PerSecond = guesses / 10,
OnlineNoThrottling10PerSecond = guesses / 1e4,
OnlineThrottling100PerHour = guesses / 1e10,
};
var crackTimesDisplay = new CrackTimesDisplay
{
- OfflineFastHashing1e10PerSecond = DisplayTime(guesses / (100 / 3600)),
+ OfflineFastHashing1e10PerSecond = DisplayTime(guesses / (100.0 / 3600)),
OfflineSlowHashing1e4PerSecond = DisplayTime(guesses / 10),
OnlineNoThrottling10PerSecond = DisplayTime(guesses / 1e4),
OnlineThrottling100PerHour = DisplayTime(guesses / 1e10),
@@ -44,7 +44,7 @@ private static string DisplayTime(double seconds)
const double day = hour * 24;
const double month = day * 31;
const double year = month * 12;
- const double century = year * 1000;
+ const double century = year * 100;
int? displayNumber = null;
string displayString;
diff --git a/zxcvbn-core/zxcvbn-core.csproj b/zxcvbn-core/zxcvbn-core.csproj
index 4007cea..960bedd 100644
--- a/zxcvbn-core/zxcvbn-core.csproj
+++ b/zxcvbn-core/zxcvbn-core.csproj
@@ -3,7 +3,7 @@
C#/.NET port of Dan Wheeler/DropBox's Zxcvbn JS password strength estimation library. Updated for .Net Core.
mickford;Tony Richards (trichards57);Dan Wheeler;DropBox
- netstandard2.0
+ netstandard2.0;net451;netstandard1.3
zxcvbn-core
zxcvbn-core
password;strength;validation;zxcvbn
@@ -37,5 +37,4 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
\ No newline at end of file
+
diff --git a/zxcvbn-cs.sln b/zxcvbn-cs.sln
index 1bcbe22..e3b53f4 100644
--- a/zxcvbn-cs.sln
+++ b/zxcvbn-cs.sln
@@ -20,7 +20,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "zxcvbn-core", "zxcvbn-core\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "zxcvbn-core-test", "zxcvbn-core-test\zxcvbn-core-test.csproj", "{65B256F9-4874-4D6F-9A46-D881FAB0215B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "zxcvbn-core-list-builder", "zxcvbn-core-list-builder\zxcvbn-core-list-builder.csproj", "{80BA1964-B98A-4D34-95B8-DFA51CD3A378}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "zxcvbn-core-list-builder", "zxcvbn-core-list-builder\zxcvbn-core-list-builder.csproj", "{80BA1964-B98A-4D34-95B8-DFA51CD3A378}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution