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);
        }
Exemple #6
0
        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);
        }
Exemple #8
0
        /// <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!"));
 }
Exemple #11
0
        /// <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);
        }
Exemple #14
0
        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);
        }
Exemple #15
0
        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);
        }
Exemple #16
0
        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);
        }
Exemple #17
0
        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);
        }
Exemple #19
0
        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));
 }