Exemplo n.º 1
0
        public static Result MostGuessableMatchSequence(string password, IEnumerable <Match> matches, bool _exclude_additive = false)
        {
            int n = password.Length;

            List <List <Match> > matchesByJ = new List <List <Match> >();

            foreach (int i in EnumerableUtility.Range(0, n))
            {
                matchesByJ.Add(new List <Match>());
            }

            foreach (Match m in matches)
            {
                matchesByJ[m.j].Add(m);
            }

            foreach (List <Match> lst in matchesByJ)
            {
                lst.Sort(delegate(Match m1, Match m2)
                {
                    return(m1.i - m2.i);
                });
            }

            Optimal optimal = new Optimal(n);

            /*
             # helper: considers whether a length-l sequence ending at match m is better (fewer guesses)
             # than previously encountered sequences, updating state if so.
             */
            void update(Match m, int l)
            {
                // helper: considers whether a length-l sequence ending at match m is better (fewer guesses)
                // than previously encountered sequences, updating state if so.
                int    k  = m.j;
                double pi = EstimateGuesses(m, password);

                if (l > 1)
                {
                    /*
                     # we're considering a length-l sequence ending with match m:
                     # obtain the product term in the minimization function by multiplying m's guesses
                     # by the product of the length-(l-1) sequence ending just before m, at m.i - 1.
                     */
                    pi *= optimal.pi[m.i - 1][l - 1];
                }
                // calculate the minimization func
                double g = factorial(l) * pi;

                if (!_exclude_additive)
                {
                    g += Math.Pow(MIN_GUESSES_BEFORE_GROWING_SEQUENCE, l - 1);
                }
                // update state if new best
                // first see if any competing sequences covering this prefix, with l or fewer matches,
                // fare better than this sequence. if so, skip it and return
                foreach (KeyValuePair <int, double> val in optimal.g[k])
                {
                    int    competingL = val.Key;
                    double competingG = val.Value;
                    if (competingL > l)
                    {
                        continue;
                    }
                    if (competingG <= g)
                    {
                        return;
                    }
                }
                optimal.g[k][l]  = g;
                optimal.m[k][l]  = m;
                optimal.pi[k][l] = pi;
            }

            // helper: make bruteforce match objects spanning i to j, inclusive.
            Match makeBruteForceMatch(int i, int j)
            {
                Match m = new Match();

                m.Token   = password.Substring(i, j - i + 1);
                m.Pattern = "bruteforce";
                m.i       = i;
                m.j       = j;
                return(m);
            }

            // helper: evaluate bruteforce matches ending at k.
            void bruteForceUpdate(int k)
            {
                // see eif a single bruteforce match spanning the k-prefix is optimal
                Match m = makeBruteForceMatch(0, k);

                update(m, 1);
                foreach (int i in EnumerableUtility.Range(1, k + 1))
                {
                    // generate k bruteforce matches, spanning from (i=1, j=k) up to (i=k, j=k)
                    // see if adding these new matches to any of the sequences in optimal[i-1]
                    // leads to new bests
                    m = makeBruteForceMatch(i, k);
                    foreach (KeyValuePair <int, Match> val in optimal.m[i - 1])
                    {
                        int l = val.Key;
                        // corner: an optimal sequence will never have two adjacent bruteforce matches
                        // it is strictly better to have a single bruteforce match spanning the same region:
                        // same contribution to the guess product with a lower length
                        // --> safe to skip those cases
                        if (val.Value.Pattern != null && val.Value.Pattern.Equals("bruteforce"))
                        {
                            continue;
                        }
                        // try adding m to this length-l sequence
                        update(m, l + 1);
                    }
                }
            }

            // helper: step backwards through optimal.m starting at the end,
            // constructing the final optimal match sequence
            List <Match> unwind(int nN)
            {
                List <Match> optimalMatchSequenceList = new List <Match>();
                int          k = nN - 1;
                // find the final best seqence length and score
                int    l = 1;
                double g = Double.MaxValue;

                foreach (KeyValuePair <int, double> val in optimal.g[k])
                {
                    int    candidateL = val.Key;
                    double candidateG = val.Value;
                    if (candidateG < g)
                    {
                        l = candidateL;
                        g = candidateG;
                    }
                }

                while (k >= 0)
                {
                    Match m = optimal.m[k][l];
                    optimalMatchSequenceList.Insert(0, m);
                    k = m.i - 1;
                    l--;
                }
                return(optimalMatchSequenceList);
            }

            foreach (int k in EnumerableUtility.Range(0, n))
            {
                foreach (Match m in matchesByJ[k])
                {
                    if (m.i > 0)
                    {
                        foreach (int l in optimal.m[m.i - 1].Keys)
                        {
                            update(m, l + 1);
                        }
                    }
                    else
                    {
                        update(m, 1);
                    }
                }
                bruteForceUpdate(k);
            }
            List <Match> optimalMatchSequence = unwind(n);
            int          optimalL             = optimalMatchSequence.Count;

            double guesses = (password.Length == 0) ? 1 : optimal.g[n - 1][optimalL];

            double seconds = guesses / Math.Pow(10, 4);

            double crackTime = PasswordScoring.GuessesToCrackTime(guesses);
            // UnityEngine.Debug.Log($"Seconds: {seconds}");

            // Debug.Log($"Score: {PasswordScoring.GuessesToScore(guesses)}");

            int score = PasswordScoring.GuessesToScore(guesses);

            return(new Result
            {
                Password = password,
                Guesses = guesses,
                GuessesLog10 = Math.Log10(guesses),
                MatchSequence = optimalMatchSequence,
                CrackTime = Math.Round(crackTime, 3),
                CrackTimeDisplay = Utility.DisplayTime(crackTime),
                Score = score
            });
        }