Example #1
0
        private void GetMatchFeedback(Match match, bool isSoleMatch, PasswordMetricResult result)
        {
            switch (match.Pattern)
            {
            case "dictionary":
                GetDictionaryMatchFeedback((DictionaryMatch)match, isSoleMatch, result);
                break;

            case "spatial":
                SpatialMatch spatialMatch = (SpatialMatch)match;

                if (spatialMatch.Turns == 1)
                {
                    result.warning = Warning.StraightRow;
                }
                else
                {
                    result.warning = Warning.ShortKeyboardPatterns;
                }

                result.suggestions.Clear();
                result.suggestions.Add(Suggestion.UseLongerKeyboardPattern);
                break;

            case "repeat":
                //todo: add support for repeated sequences longer than 1 char
                //  if(match.Token.Length == 1)
                result.warning = Warning.RepeatsLikeAaaEasy;
                //  else
                //       result.warning = Warning.RepeatsLikeAbcSlighterHarder;

                result.suggestions.Clear();
                result.suggestions.Add(Suggestion.AvoidRepeatedWordsAndChars);
                break;

            case "sequence":
                result.warning = Warning.SequenceAbcEasy;

                result.suggestions.Clear();
                result.suggestions.Add(Suggestion.AvoidSequences);
                break;

            //todo: add support for recent_year, however not example exist on https://dl.dropboxusercontent.com/u/209/zxcvbn/test/index.html


            case "date":
                result.warning = Warning.DatesEasy;

                result.suggestions.Clear();
                result.suggestions.Add(Suggestion.AvoidDatesYearsAssociatedYou);
                break;
            }
        }
Example #2
0
        /// <summary>
        /// Returns a new result structure initialised with data for the lowest entropy result of all of the matches passed in, adding brute-force
        /// matches where there are no lesser entropy found pattern matches.
        /// </summary>
        /// <param name="matches">Password being evaluated</param>
        /// <param name="password">List of matches found against the password</param>
        /// <returns>A result object for the lowest entropy match sequence</returns>
        private PasswordMetricResult FindMinimumEntropyMatch(string password, IEnumerable <Match> matches)
        {
            var bruteforce_cardinality = PasswordScoring.PasswordCardinality(password);

            // Minimum entropy up to position k in the password
            var minimumEntropyToIndex = new double[password.Length];
            var bestMatchForIndex     = new Match[password.Length];

            for (var k = 0; k < password.Length; k++)
            {
                // Start with bruteforce scenario added to previous sequence to beat
                minimumEntropyToIndex[k] = (k == 0 ? 0 : minimumEntropyToIndex[k - 1]) + Math.Log(bruteforce_cardinality, 2);

                // All matches that end at the current character, test to see if the entropy is less
                foreach (var match in matches.Where(m => m.End == k))
                {
                    var candidate_entropy = (match.Begin <= 0 ? 0 : minimumEntropyToIndex[match.Begin - 1]) + match.Entropy;
                    if (candidate_entropy < minimumEntropyToIndex[k])
                    {
                        minimumEntropyToIndex[k] = candidate_entropy;
                        bestMatchForIndex[k]     = match;
                    }
                }
            }


            // Walk backwards through lowest entropy matches, to build the best password sequence
            var matchSequence = new List <Match>();

            for (var k = password.Length - 1; k >= 0; k--)
            {
                if (bestMatchForIndex[k] != null)
                {
                    matchSequence.Add(bestMatchForIndex[k]);
                    k = bestMatchForIndex[k].Begin; // Jump back to start of match
                }
            }
            matchSequence.Reverse();


            // The match sequence might have gaps, fill in with bruteforce matching
            // After this the matches in matchSequence must cover the whole string (i.e. match[k].j == match[k + 1].i - 1)
            if (matchSequence.Count == 0)
            {
                // To make things easy, we'll separate out the case where there are no matches so everything is bruteforced
                matchSequence.Add(new Match()
                {
                    Begin       = 0,
                    End         = password.Length,
                    Token       = password,
                    Cardinality = bruteforce_cardinality,
                    Pattern     = BruteforcePattern,
                    Entropy     = Math.Log(Math.Pow(bruteforce_cardinality, password.Length), 2)
                });
            }
            else
            {
                // There are matches, so find the gaps and fill them in
                var matchSequenceCopy = new List <Match>();
                for (var k = 0; k < matchSequence.Count; k++)
                {
                    var m1 = matchSequence[k];
                    var m2 = (k < matchSequence.Count - 1 ? matchSequence[k + 1] : new Match()
                    {
                        Begin = password.Length
                    });                                                                                                      // Next match, or a match past the end of the password

                    matchSequenceCopy.Add(m1);
                    if (m1.End < m2.Begin - 1)
                    {
                        // Fill in gap
                        var ns = m1.End + 1;
                        var ne = m2.Begin - 1;
                        matchSequenceCopy.Add(new Match()
                        {
                            Begin       = ns,
                            End         = ne,
                            Token       = password.Substring(ns, ne - ns + 1),
                            Cardinality = bruteforce_cardinality,
                            Pattern     = BruteforcePattern,
                            Entropy     = Math.Log(Math.Pow(bruteforce_cardinality, ne - ns + 1), 2)
                        });
                    }
                }

                matchSequence = matchSequenceCopy;
            }


            var minEntropy = (password.Length == 0 ? 0 : minimumEntropyToIndex[password.Length - 1]);
            var crackTime  = PasswordScoring.EntropyToCrackTime(minEntropy);

            var result = new PasswordMetricResult();

            result.Password         = password;
            result.Entropy          = Math.Round(minEntropy, 3);
            result.MatchSequence    = matchSequence;
            result.CrackTime        = Math.Round(crackTime, 3);
            result.CrackTimeDisplay = Utility.DisplayTime(crackTime, this.translation);
            result.Score            = PasswordScoring.CrackTimeToScore(crackTime);


            //starting feedback
            if ((matchSequence == null) || (matchSequence.Count() == 0))
            {
                result.warning = Warning.Default;
                result.suggestions.Clear();
                result.suggestions.Add(Suggestion.Default);
            }
            else
            {
                //no Feedback if score is good or great
                if (result.Score > 2)
                {
                    result.warning = Warning.Empty;
                    result.suggestions.Clear();
                    result.suggestions.Add(Suggestion.Empty);
                }
                else
                {
                    //tie feedback to the longest match for longer sequences
                    Match longestMatch = GetLongestMatch(matchSequence);
                    GetMatchFeedback(longestMatch, matchSequence.Count() == 1, result);
                    result.suggestions.Insert(0, Suggestion.AddAnotherWordOrTwo);
                }
            }
            return(result);
        }
Example #3
0
        private void GetDictionaryMatchFeedback(DictionaryMatch match, bool isSoleMatch, PasswordMetricResult result)
        {
            if (match.DictionaryName.Equals("passwords"))
            {
                //todo: add support for reversed words
                if (isSoleMatch == true && !(match is L33tDictionaryMatch))
                {
                    if (match.Rank <= 10)
                    {
                        result.warning = Warning.Top10Passwords;
                    }
                    else if (match.Rank <= 100)
                    {
                        result.warning = Warning.Top100Passwords;
                    }
                    else
                    {
                        result.warning = Warning.CommonPasswords;
                    }
                }
                else if (PasswordScoring.CrackTimeToScore(PasswordScoring.EntropyToCrackTime(match.Entropy)) <= 1)
                {
                    result.warning = Warning.SimilarCommonPasswords;
                }
            }
            else if (match.DictionaryName.Equals("english"))
            {
                if (isSoleMatch == true)
                {
                    result.warning = Warning.WordEasy;
                }
            }
            else if (match.DictionaryName.Equals("surnames") ||
                     match.DictionaryName.Equals("male_names") ||
                     match.DictionaryName.Equals("female_names"))
            {
                if (isSoleMatch == true)
                {
                    result.warning = Warning.NameSurnamesEasy;
                }
                else
                {
                    result.warning = Warning.CommonNameSurnamesEasy;
                }
            }
            else
            {
                result.warning = Warning.Empty;
            }

            string word = match.Token;

            if (Regex.IsMatch(word, PasswordScoring.StartUpper))
            {
                result.suggestions.Add(Suggestion.CapsDontHelp);
            }
            else if (Regex.IsMatch(word, PasswordScoring.AllUpper) && !word.Equals(word.ToLowerInvariant()))
            {
                result.suggestions.Add(Suggestion.AllCapsEasy);
            }

            //todo: add support for reversed words
            //if match.reversed and match.token.length >= 4
            //    suggestions.push "Reversed words aren't much harder to guess"

            if (match is L33tDictionaryMatch)
            {
                result.suggestions.Add(Suggestion.PredictableSubstitutionsEasy);
            }
        }