示例#1
0
        private static double EstimateGuesses(ZxcvbnMatch match, string password)
        {
            if (match.Guesses.HasValue)
            {
                return(match.Guesses.Value);
            }

            int minGuesses = 1;

            if (match.Token.Length < password.Length)
            {
                minGuesses = match.Token.Length == 1 ? minSubmatchGuessesSingleChar : minSubmatchGuessesMultiChar;
            }

            double guesses = match.Pattern switch
            {
                MatchPattern.Dictionary => DictionaryGuesses(match),
                MatchPattern.Spatial => SpatialGuesses(match),
                MatchPattern.Repeat => RepeatGuesses(match),
                MatchPattern.Sequence => SequenceGuesses(match),
                MatchPattern.Regex => RegexGuesses(match),
                MatchPattern.Date => DateGuesses(match),
                _ => BruteforceGuesses(match)
            };

            match.Guesses      = Math.Max(guesses, minGuesses);
            match.GuessesLog10 = Log10(match.Guesses.Value);

            return(match.Guesses.Value);
        }
示例#2
0
 private static double RegexGuesses(ZxcvbnMatch match)
 {
     return(match.RegexName switch
     {
         RegexName.RecentYear => Math.Max(Math.Abs(int.Parse(match.RegexMatch.Value) - referenceYear), minYearSpace),
         RegexName.None => 0,
         _ => Math.Pow((int)match.RegexName, match.Token.Length)
     });
示例#3
0
        private static double SequenceGuesses(ZxcvbnMatch match)
        {
            char   firstChar   = match.Token[0];
            double baseGuesses = obviousStartingPoints.Contains(match.Token[0]) ? 4 : (char.IsDigit(firstChar) ? 10 : 26);

            if (!match.Ascending)
            {
                baseGuesses *= 2;
            }

            return(baseGuesses * match.Token.Length);
        }
示例#4
0
 private static Feedback GetMatchFeedback(ZxcvbnMatch match, bool isSoleMatch)
 {
     return(match.Pattern switch
     {
         MatchPattern.Dictionary => GetDictionaryMatchFeedback(match, isSoleMatch),
         MatchPattern.Spatial => new Feedback(match.Turns == 1 ? "Straight rows of keys are easy to guess" : "Short keyboard patterns are easy to guess", "Use a longer keyboard pattern with more turns"),
         MatchPattern.Repeat => new Feedback(match.BaseToken.Length == 1 ? @"Repeats like ""aaa"" are easy to guess" : @"Repeats like ""abcabcabc"" are only slightly harder to guess than ""abc""", "Avoid repeated words and characters"),
         MatchPattern.Sequence => new Feedback("Sequences like abc or 6543 are easy to guess", "Avoid sequences"),
         MatchPattern.Regex => match.RegexName == RegexName.RecentYear ? new Feedback("Recent years are easy to guess", "Avoid recent years", "Avoid years that are associated with you") : Default,
         MatchPattern.Date => new Feedback("Dates are often easy to guess", "Avoid dates and years that are associated with you"),
         _ => Empty
     });
示例#5
0
        private static double BruteforceGuesses(ZxcvbnMatch match)
        {
            double guesses = Math.Pow(bruteforceCardinality, match.Token.Length);

            if (double.IsInfinity(guesses))
            {
                guesses = double.MaxValue;
            }

            double minGuesses = match.Token.Length == 1 ? minSubmatchGuessesSingleChar : minSubmatchGuessesMultiChar;

            minGuesses += 1;

            return(Math.Max(guesses, minGuesses));
        }
示例#6
0
        internal static Feedback GetFeedback(double score, ZxcvbnMatch[] sequence)
        {
            if (sequence == null || sequence.Length == 0)
            {
                return(Default);
            }

            if (score > 2)
            {
                return(Empty);
            }

            ZxcvbnMatch longestMatch = sequence.OrderByDescending(m => m.Token.Length).First();

            Feedback feedback = GetMatchFeedback(longestMatch, sequence.Length == 1);

            feedback.Suggestions = feedback.Suggestions.Concat(new[] { "Add another word or two. Uncommon words are better." }).ToArray();

            return(feedback);
        }
示例#7
0
        public static ZxcvbnResult MostGuessableMatchSequence(string password, IEnumerable <ZxcvbnMatch> matches, bool excludeAdditive = false)
        {
            List <ZxcvbnMatch>[] matchesByJ = Enumerable.Range(0, password.Length).Select(i => new List <ZxcvbnMatch>()).ToArray();

            for (int i = 0; i < password.Length; i++)
            {
                matchesByJ[i] = new List <ZxcvbnMatch>();
            }

            foreach (ZxcvbnMatch m in matches)
            {
                matchesByJ[m.J].Add(m);
            }

            foreach (List <ZxcvbnMatch> lst in matchesByJ)
            {
                lst.Sort((m1, m2) => m1.I - m2.I);
            }

            Dictionary <int, ZxcvbnMatch>[] optimalM  = Enumerable.Range(0, password.Length).Select(i => new Dictionary <int, ZxcvbnMatch>()).ToArray();
            Dictionary <int, double>[]      optimalPi = Enumerable.Range(0, password.Length).Select(i => new Dictionary <int, double>()).ToArray();
            Dictionary <int, double>[]      optimalG  = Enumerable.Range(0, password.Length).Select(i => new Dictionary <int, double>()).ToArray();

            void Update(ZxcvbnMatch m, int l)
            {
                int    k  = m.J;
                double pi = EstimateGuesses(m, password);

                if (l > 1)
                {
                    pi *= optimalPi[m.I - 1][l - 1];
                }

                double g = Factorial(l) * pi;

                if (!excludeAdditive)
                {
                    g += Math.Pow(minGuessesBeforeGrowingSequence, l - 1);
                }

                foreach (KeyValuePair <int, double> kv in optimalG[k])
                {
                    int    competingL = kv.Key;
                    double competingG = kv.Value;

                    if (competingL > l)
                    {
                        continue;
                    }
                    else if (competingG <= g)
                    {
                        return;
                    }
                }

                optimalG[k].AddOrSet(l, g);
                optimalM[k].AddOrSet(l, m);
                optimalPi[k].AddOrSet(l, pi);
            }

            void BruteforceUpdate(int k)
            {
                ZxcvbnMatch m = MakeBruteforceMatch(0, k);

                Update(m, 1);

                for (int i = 1; i <= k; i++)
                {
                    m = MakeBruteforceMatch(i, k);

                    foreach (KeyValuePair <int, ZxcvbnMatch> kv in optimalM[i - 1])
                    {
                        int         l     = kv.Key;
                        ZxcvbnMatch lastM = kv.Value;

                        if (lastM.Pattern == MatchPattern.Bruteforce)
                        {
                            continue;
                        }

                        Update(m, l + 1);
                    }
                }
            }

            ZxcvbnMatch MakeBruteforceMatch(int i, int j) => new ZxcvbnMatch
            {
                Pattern = MatchPattern.Bruteforce,
                Token   = password.Substring(i, j - i + 1),
                I       = i,
                J       = j
            };

            ZxcvbnMatch[] Unwind(int n)
            {
                List <ZxcvbnMatch> optimalMatchSequence = new List <ZxcvbnMatch>();

                int    k = n - 1;
                int    l = 0;
                double g = double.PositiveInfinity;

                foreach (KeyValuePair <int, double> kv in optimalG[k])
                {
                    int    candidateL = kv.Key;
                    double candidateG = kv.Value;

                    if (candidateG < g)
                    {
                        l = candidateL;
                        g = candidateG;
                    }
                }

                while (k >= 0)
                {
                    ZxcvbnMatch m = optimalM[k][l];
                    optimalMatchSequence.Insert(0, m);
                    k = m.I - 1;
                    l--;
                }

                return(optimalMatchSequence.ToArray());
            }

            for (int k = 0; k < password.Length; k++)
            {
                foreach (ZxcvbnMatch m in matchesByJ[k])
                {
                    if (m.I > 0)
                    {
                        foreach (int l in optimalM[m.I - 1].Keys.OrderBy(k => k))
                        {
                            Update(m, l + 1);
                        }
                    }
                    else
                    {
                        Update(m, 1);
                    }
                }

                BruteforceUpdate(k);
            }

            ZxcvbnMatch[] optimalMatchSequence = Unwind(password.Length);
            int           optimalL             = optimalMatchSequence.Length;
            double        guesses = password.Length == 0 ? 1 : optimalG[password.Length - 1][optimalL];

            return(new ZxcvbnResult
            {
                Password = password,
                Guesses = guesses,
                GuessesLog10 = Log10(guesses),
                Sequence = optimalMatchSequence
            });
        }
示例#8
0
 private static double RepeatGuesses(ZxcvbnMatch match)
 {
     return(match.BaseGuesses * match.RepeatCount);
 }