public void MostGuessableMatchSequenceChoosesOptimalMatches() { var password = "******"; var m0 = CreateTestMatch(0, 9, 3); var m1 = CreateTestMatch(0, 3, 2); var m2 = CreateTestMatch(4, 9, 1); var expected = new List <Match> { m0, }; var result = PasswordScoring.MostGuessableMatchSequence(password, new List <Match> { m0, m1, m2 }, true); result.Sequence.Should().BeEquivalentTo(expected); m0.Guesses = 5; expected = new List <Match> { m1, m2, }; result = PasswordScoring.MostGuessableMatchSequence(password, new List <Match> { m0, m1, m2 }, true); result.Sequence.Should().BeEquivalentTo(expected); }
/// <summary> /// Calculates the number of l33t variations in the word. /// </summary> /// <param name="match">The match.</param> /// <returns>The number of possible variations.</returns> internal static double L33tVariations(DictionaryMatch match) { if (!match.L33t) { return(1); } var variations = 1.0; foreach (var subbed in match.Sub.Keys) { var unsubbed = match.Sub[subbed]; var s = match.Token.ToLower().Count(c => c == subbed); var u = match.Token.ToLower().Count(c => c == unsubbed); if (s == 0 || u == 0) { variations *= 2; } else { var p = Math.Min(u, s); var possibilities = 0.0; for (var i = 1; i <= p; i++) { possibilities += PasswordScoring.Binomial(u + s, i); } variations *= possibilities; } } return(variations); }
public void AccountsForTurnPositionsDirectionsAndStartingKey() { var match = new SpatialMatch { Token = "zxcvbn", Graph = "qwerty", Turns = 3, ShiftedCount = 0, i = 1, j = 2, }; var l = match.Token.Length; var s = SpatialGuessesCalculator.KeyboardStartingPositions; var d = SpatialGuessesCalculator.KeyboardAverageDegree; var expected = 0.0; for (var i = 2; i <= l; i++) { for (var j = 1; j <= Math.Min(match.Turns, i - 1); j++) { expected += PasswordScoring.Binomial(i - 1, j - 1) * s * Math.Pow(d, j); } } var actual = SpatialGuessesCalculator.CalculateGuesses(match); actual.Should().Be(expected); }
// ReSharper disable once InconsistentNaming private static void CalulateL33tEntropy(L33tDictionaryMatch match) { // I'm a bit dubious about this function, but I have duplicated zxcvbn functionality regardless var possibilities = 0; foreach (var kvp in match.Subs) { var subbedChars = match.Token.Count(c => c == kvp.Key); var unsubbedChars = match.Token.Count(c => c == kvp.Value); // Won't this always be zero? possibilities += Enumerable.Range(0, Math.Min(subbedChars, unsubbedChars) + 1).Sum(i => (int)PasswordScoring.Binomial(subbedChars + unsubbedChars, i)); } var entropy = Math.Log(possibilities, 2); // In the case of only a single subsitution (e.g. 4pple) this would otherwise come out as zero, so give it one bit match.L33tEntropy = (entropy < 1 ? 1 : entropy); match.Entropy += match.L33tEntropy; // We have to recalculate the uppercase entropy -- the password matcher will have used the subbed password not the original text match.Entropy -= match.UppercaseEntropy; match.UppercaseEntropy = PasswordScoring.CalculateUppercaseEntropy(match.Token); match.Entropy += match.UppercaseEntropy; }
/// <summary> /// Calculates the number of uppercase variations in the word. /// </summary> /// <param name="token">The token.</param> /// <returns>The number of possible variations.</returns> internal static double UppercaseVariations(string 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))) || token.All(c => char.IsUpper(c)) || (char.IsUpper(token.Last()) && token.Take(token.Length - 1).All(c => char.IsLower(c)))) { return(2); } var u = token.Count(c => char.IsUpper(c)); var l = token.Count(c => char.IsLower(c)); var variations = 0.0; for (var i = 1; i <= Math.Min(u, l); i++) { variations += PasswordScoring.Binomial(u + l, i); } return(variations); }
private void CalculateEntropyForMatch(DictionaryMatch match) { match.BaseEntropy = Math.Log(match.Rank, 2); match.UppercaseEntropy = PasswordScoring.CalculateUppercaseEntropy(match.Token); match.Entropy = match.BaseEntropy + match.UppercaseEntropy; }
public void MostGuessableMatchSequenceReturnsBruteForceThenMatchThenBruteForceWhenMatchCoversAnInfixOfPassword() { var password = "******"; var existingMatch = CreateTestMatch(1, 8, 1); var expected = new List <Match> { new BruteForceMatch { i = 0, j = 0, Token = "0", Guesses = 11, }, existingMatch, new BruteForceMatch { i = 9, j = 9, Token = "9", Guesses = 11, }, }; var result = PasswordScoring.MostGuessableMatchSequence(password, new List <Match> { existingMatch }, true); result.Sequence.Should().BeEquivalentTo(expected); }
/// <summary> /// Find repeat matches in <paramref name="password" />. /// </summary> /// <param name="password">The password to check.</param> /// <returns>An enumerable of repeat matches.</returns> public IEnumerable <Matches.Match> MatchPassword(string password) { var matches = new List <Matches.Match>(); var greedy = "(.+)\\1+"; var lazy = "(.+?)\\1+"; var lazyAnchored = "^(.+?)\\1+$"; var lastIndex = 0; while (lastIndex < password.Length) { var greedyMatch = Regex.Match(password.Substring(lastIndex), greedy); var lazyMatch = Regex.Match(password.Substring(lastIndex), lazy); if (!greedyMatch.Success) { break; } System.Text.RegularExpressions.Match match; string baseToken; if (greedyMatch.Length > lazyMatch.Length) { match = greedyMatch; baseToken = Regex.Match(match.Value, lazyAnchored).Groups[1].Value; } else { match = lazyMatch; baseToken = match.Groups[1].Value; } var i = lastIndex + match.Index; var j = lastIndex + match.Index + match.Length - 1; var baseAnalysis = PasswordScoring.MostGuessableMatchSequence(baseToken, Core.GetAllMatches(baseToken)); var baseMatches = baseAnalysis.Sequence; var baseGuesses = baseAnalysis.Guesses; var m = new RepeatMatch { i = i, j = j, Token = match.Value, BaseToken = baseToken, BaseGuesses = baseGuesses, RepeatCount = match.Length / baseToken.Length, }; m.BaseMatchItems.AddRange(baseMatches); matches.Add(m); lastIndex = j + 1; } return(matches); }
public void BinomialTest() { Assert.AreEqual(1, PasswordScoring.Binomial(0, 0)); Assert.AreEqual(1, PasswordScoring.Binomial(1, 0)); Assert.AreEqual(0, PasswordScoring.Binomial(0, 1)); Assert.AreEqual(1, PasswordScoring.Binomial(1, 1)); Assert.AreEqual(56, PasswordScoring.Binomial(8, 3)); Assert.AreEqual(2598960, PasswordScoring.Binomial(52, 5)); }
public void BruteForceCardinalityTest() { Assert.AreEqual(26, PasswordScoring.PasswordCardinality("asdf")); Assert.AreEqual(26, PasswordScoring.PasswordCardinality("ASDF")); Assert.AreEqual(52, PasswordScoring.PasswordCardinality("aSDf")); Assert.AreEqual(10, PasswordScoring.PasswordCardinality("124890")); Assert.AreEqual(62, PasswordScoring.PasswordCardinality("aS159Df")); Assert.AreEqual(33, PasswordScoring.PasswordCardinality("!@<%:{$:#<@}{+&)(*%")); Assert.AreEqual(100, PasswordScoring.PasswordCardinality("©")); Assert.AreEqual(95, PasswordScoring.PasswordCardinality("ThisIs@T3stP4ssw0rd!")); }
/// <summary> /// Estimates the attempts required to guess the password. /// </summary> /// <param name="match">The match.</param> /// <returns>The guesses estimate.</returns> public static double CalculateGuesses(SpatialMatch match) { int s; double d; if (match.Graph == "qwerty" || match.Graph == "dvorak") { s = KeyboardStartingPositions; d = KeyboardAverageDegree; } else { s = KeypadStartingPositions; d = KeypadAverageDegree; } double guesses = 0; var l = match.Token.Length; var t = match.Turns; for (var i = 2; i <= l; i++) { var possibleTurns = Math.Min(t, i - 1); for (var j = 1; j <= possibleTurns; j++) { guesses += PasswordScoring.Binomial(i - 1, j - 1) * s * Math.Pow(d, j); } } if (match.ShiftedCount > 0) { var shifted = match.ShiftedCount; var unshifted = match.Token.Length - match.ShiftedCount; if (shifted == 0 || unshifted == 0) { guesses *= 2; } else { double variations = 0; for (var i = 1; i <= Math.Min(shifted, unshifted); i++) { variations += PasswordScoring.Binomial(shifted + unshifted, i); } guesses *= variations; } } return(guesses); }
public void IsDelegatedToByEstimateGuesses() { var match = new RegexMatch { Token = "1972", RegexName = "recent_year", i = 1, j = 2, }; var result = PasswordScoring.EstimateGuesses(match, "1972"); result.Should().Be(DateMatcher.ReferenceYear - 1972); }
public void MostGuessableMatchSequenceRetursnOneMatchForEmptySequence() { var password = "******"; var expected = new List <Match> { new BruteForceMatch { i = 0, j = 9, Token = password, Guesses = 10000000000, }, }; var result = PasswordScoring.MostGuessableMatchSequence(password, Enumerable.Empty <Match>()); result.Sequence.Should().BeEquivalentTo(expected); }
public void ReturnsCachedGuessesIfAvailable() { var match = new DateMatch { Guesses = 1, Token = "1977", Year = 1977, Month = 8, Day = 14, Separator = "/", i = 1, j = 2, }; var actual = PasswordScoring.EstimateGuesses(match, string.Empty); actual.Should().Be(1); }
public void IsDelegatedToByEstimateGuesses() { var match = new SpatialMatch { Token = "zxcvbn", Graph = "qwerty", Turns = 1, ShiftedCount = 0, i = 1, j = 2, }; var expected = SpatialGuessesCalculator.KeyboardStartingPositions * SpatialGuessesCalculator.KeyboardAverageDegree * (match.Token.Length - 1); var actual = PasswordScoring.EstimateGuesses(match, match.Token); actual.Should().Be(expected); }
public void IsDelegatedToByEstimateGuesses() { var match = new RepeatMatch { Token = "aa", BaseToken = "a", BaseGuesses = PasswordScoring.MostGuessableMatchSequence("a", Core.GetAllMatches("a")).Guesses, BaseMatchItems = new List <Match>(), RepeatCount = 2, i = 1, j = 2, }; var expected = RepeatGuessesCalculator.CalculateGuesses(match); var actual = PasswordScoring.EstimateGuesses(match, "aa"); actual.Should().Be(expected); }
public void IsDelegatedToBeEstimateGuesses() { var match = new DateMatch { Token = "1923", Separator = string.Empty, Year = 1923, Month = 1, Day = 1, i = 1, j = 2, }; var actual = PasswordScoring.EstimateGuesses(match, "1923"); var expected = 365 * (DateMatcher.ReferenceYear - match.Year); actual.Should().Be(expected); }
public void IsDelegatedToByEstimateGuesses() { var match = new DictionaryMatch { Token = "aaaaa", Rank = 32, DictionaryName = "dic", i = 1, j = 2, L33t = false, MatchedWord = "a", Reversed = false, }; var expected = 32; var actual = PasswordScoring.EstimateGuesses(match, "aaaaa"); actual.Should().Be(expected); }
public void CalculatesTheRightNumberOfGuesses(string token, string baseToken, int expectedRepeats) { var baseGuesses = PasswordScoring.MostGuessableMatchSequence(baseToken, Core.GetAllMatches(baseToken)).Guesses; var match = new RepeatMatch { Token = token, BaseToken = baseToken, BaseGuesses = baseGuesses, RepeatCount = expectedRepeats, BaseMatchItems = new List <Match>(), i = 1, j = 2, }; var expected = baseGuesses * expectedRepeats; var actual = RepeatGuessesCalculator.CalculateGuesses(match); actual.Should().Be(expected); }
public void MostGuessableMatchSequenceChoosesMatchWithFewestGuessesIfTheyMatchTheSameSpan() { var password = "******"; var worseMatch = CreateTestMatch(0, 9, 1); var bestMatch = CreateTestMatch(0, 9, 2); var expected = new List <Match> { worseMatch, }; var result = PasswordScoring.MostGuessableMatchSequence(password, new List <Match> { worseMatch, bestMatch }, true); result.Sequence.Should().BeEquivalentTo(expected); result = PasswordScoring.MostGuessableMatchSequence(password, new List <Match> { bestMatch, worseMatch }, true); result.Sequence.Should().BeEquivalentTo(expected); }
/// <summary> /// Calculate entropy for a math that was found on this adjacency graph /// </summary> public double CalculateEntropy(int matchLength, int turns, int shiftedCount) { // This is an estimation of the number of patterns with length of matchLength or less with turns turns or less var possibilities = Enumerable.Range(2, matchLength - 1).Sum(i => { var possible_turns = Math.Min(turns, i - 1); return(Enumerable.Range(1, possible_turns).Sum(j => { return StartingPositions * Math.Pow(AverageDegree, j) * PasswordScoring.Binomial(i - 1, j - 1); })); }); var entropy = Math.Log(possibilities, 2); // Entropy increaeses for a mix of shifted and unshifted if (shiftedCount > 0) { var unshifted = matchLength - shiftedCount; entropy += Math.Log(Enumerable.Range(0, Math.Min(shiftedCount, unshifted) + 1).Sum(i => PasswordScoring.Binomial(matchLength, i)), 2); } return(entropy); }
public void PasswordScoringPasswordCardinalityScoresCorrectly(string password, int score) { PasswordScoring.PasswordCardinality(password).Should().Be(score); }
public void PasswordScoringBinomialScoresCorrectly(int n, int k, int score) { PasswordScoring.Binomial(n, k).Should().Be(score); }
/// <summary> /// Match substrings of password agains the loaded dictionary /// </summary> /// <param name="password">The password to match</param> /// <param name="cancellationToken">The token for the operation</param> /// <returns>An enumerable of dictionary matches</returns> /// <seealso cref="DictionaryMatch"/> public IEnumerable <Match> MatchPassword(string password, CancellationToken cancellationToken) { // Build the dictionary to use if necessary DictionarySemaphore.Wait(cancellationToken); // Build the dictionary if (_RankedDictionary == null) { _RankedDictionary = BuildRankedDictionary(DictionaryPath); } DictionarySemaphore.Release(); // Check the token state cancellationToken.ThrowIfCancellationRequested(); string passwordLower = password.ToLower(); // Compute and return the result list return ((from i in Enumerable.Range(0, password.Length) from j in Enumerable.Range(i, password.Length - i) let psub = passwordLower.Substring(i, j - i + 1) where _RankedDictionary.ContainsKey(psub) let rank = _RankedDictionary[psub] let token = password.Substring(i, j - i + 1) // Could have different case so pull from password let baseEntropy = Math.Log(rank, 2) let upperEntropy = PasswordScoring.CalculateUppercaseEntropy(token) select new DictionaryMatch { Pattern = DictionaryPattern, i = i, j = j, Token = token, MatchedWord = psub, Rank = rank, DictionaryName = DictionaryName, Cardinality = _RankedDictionary.Count, BaseEntropy = baseEntropy, UppercaseEntropy = upperEntropy, Entropy = baseEntropy + upperEntropy }).ToList()); }
private double CalculateEntropy(string match) { return(Math.Log(PasswordScoring.PasswordCardinality(match) * match.Length, 2)); }