/// <summary> /// Estimate the quality of a password. /// </summary> /// <param name="vPasswordChars">Password to check.</param> /// <returns>Estimated bit-strength of the password.</returns> public static uint EstimatePasswordBits(char[] vPasswordChars) { if(vPasswordChars == null) { Debug.Assert(false); return 0; } if(vPasswordChars.Length == 0) return 0; EnsureInitialized(); int n = vPasswordChars.Length; List<QePatternInstance>[] vPatterns = new List<QePatternInstance>[n]; for(int i = 0; i < n; ++i) { vPatterns[i] = new List<QePatternInstance>(); QePatternInstance piChar = new QePatternInstance(i, 1, GetCharType(vPasswordChars[i])); vPatterns[i].Add(piChar); } FindRepetitions(vPasswordChars, vPatterns); FindNumbers(vPasswordChars, vPatterns); FindDiffSeqs(vPasswordChars, vPatterns); FindPopularPasswords(vPasswordChars, vPatterns); // Encoders must not be static, because the entropy estimation // may run concurrently in multiple threads and the encoders are // not read-only EntropyEncoder ecPattern = new EntropyEncoder(PatternID.All, 0, 1, 0); MultiEntropyEncoder mcData = new MultiEntropyEncoder(); for(int i = 0; i < (m_lCharTypes.Count - 1); ++i) { // Let m be the alphabet size. In order to ensure that two same // characters cost at least as much as a single character, for // the probability p and weight w of the character it must hold: // -log(1/m) >= -2*log(p) // <=> log(1/m) <= log(p^2) <=> 1/m <= p^2 <=> p >= sqrt(1/m); // sqrt(1/m) = (1+w)/(m+w) // <=> m+w = (1+w)*sqrt(m) <=> m+w = sqrt(m) + w*sqrt(m) // <=> w*(1-sqrt(m)) = sqrt(m) - m <=> w = (sqrt(m)-m)/(1-sqrt(m)) // <=> w = (sqrt(m)-m)*(1+sqrt(m))/(1-m) // <=> w = (sqrt(m)-m+m-m*sqrt(m))/(1-m) <=> w = sqrt(m) ulong uw = (ulong)Math.Sqrt((double)m_lCharTypes[i].CharCount); mcData.AddEncoder(m_lCharTypes[i].TypeID, new EntropyEncoder( m_lCharTypes[i].Alphabet, 1, uw, 1)); } double dblMinCost = (double)int.MaxValue; int tStart = Environment.TickCount; Stack<QePathState> sRec = new Stack<QePathState>(); sRec.Push(new QePathState(0, new List<QePatternInstance>())); while(sRec.Count > 0) { int tDiff = Environment.TickCount - tStart; if(tDiff > 500) break; QePathState s = sRec.Pop(); if(s.Position >= n) { Debug.Assert(s.Position == n); double dblCost = ComputePathCost(s.Path, vPasswordChars, ecPattern, mcData); if(dblCost < dblMinCost) dblMinCost = dblCost; } else { List<QePatternInstance> lSubs = vPatterns[s.Position]; for(int i = lSubs.Count - 1; i >= 0; --i) { QePatternInstance pi = lSubs[i]; Debug.Assert(pi.Position == s.Position); Debug.Assert(pi.Length >= 1); List<QePatternInstance> lNewPath = new List<QePatternInstance>(s.Path.Count + 1); lNewPath.AddRange(s.Path); lNewPath.Add(pi); Debug.Assert(lNewPath.Capacity == (s.Path.Count + 1)); QePathState sNew = new QePathState(s.Position + pi.Length, lNewPath); sRec.Push(sNew); } } } return (uint)Math.Ceiling(dblMinCost); }
/// <summary> /// Estimate the quality of a password. /// </summary> /// <param name="vPasswordChars">Password to check.</param> /// <returns>Estimated bit-strength of the password.</returns> public static uint EstimatePasswordBits(char[] vPasswordChars) { if (vPasswordChars == null) { Debug.Assert(false); return(0); } if (vPasswordChars.Length == 0) { return(0); } EnsureInitialized(); int n = vPasswordChars.Length; List <QePatternInstance>[] vPatterns = new List <QePatternInstance> [n]; for (int i = 0; i < n; ++i) { vPatterns[i] = new List <QePatternInstance>(); QePatternInstance piChar = new QePatternInstance(i, 1, GetCharType(vPasswordChars[i])); vPatterns[i].Add(piChar); } FindRepetitions(vPasswordChars, vPatterns); FindNumbers(vPasswordChars, vPatterns); FindDiffSeqs(vPasswordChars, vPatterns); FindPopularPasswords(vPasswordChars, vPatterns); // Encoders must not be static, because the entropy estimation // may run concurrently in multiple threads and the encoders are // not read-only EntropyEncoder ecPattern = new EntropyEncoder(PatternID.All, 0, 1, 0); MultiEntropyEncoder mcData = new MultiEntropyEncoder(); for (int i = 0; i < (m_lCharTypes.Count - 1); ++i) { // Let m be the alphabet size. In order to ensure that two same // characters cost at least as much as a single character, for // the probability p and weight w of the character it must hold: // -log(1/m) >= -2*log(p) // <=> log(1/m) <= log(p^2) <=> 1/m <= p^2 <=> p >= sqrt(1/m); // sqrt(1/m) = (1+w)/(m+w) // <=> m+w = (1+w)*sqrt(m) <=> m+w = sqrt(m) + w*sqrt(m) // <=> w*(1-sqrt(m)) = sqrt(m) - m <=> w = (sqrt(m)-m)/(1-sqrt(m)) // <=> w = (sqrt(m)-m)*(1+sqrt(m))/(1-m) // <=> w = (sqrt(m)-m+m-m*sqrt(m))/(1-m) <=> w = sqrt(m) ulong uw = (ulong)Math.Sqrt((double)m_lCharTypes[i].CharCount); mcData.AddEncoder(m_lCharTypes[i].TypeID, new EntropyEncoder( m_lCharTypes[i].Alphabet, 1, uw, 1)); } double dblMinCost = (double)int.MaxValue; int tStart = Environment.TickCount; Stack <QePathState> sRec = new Stack <QePathState>(); sRec.Push(new QePathState(0, new List <QePatternInstance>())); while (sRec.Count > 0) { int tDiff = Environment.TickCount - tStart; if (tDiff > 500) { break; } QePathState s = sRec.Pop(); if (s.Position >= n) { Debug.Assert(s.Position == n); double dblCost = ComputePathCost(s.Path, vPasswordChars, ecPattern, mcData); if (dblCost < dblMinCost) { dblMinCost = dblCost; } } else { List <QePatternInstance> lSubs = vPatterns[s.Position]; for (int i = lSubs.Count - 1; i >= 0; --i) { QePatternInstance pi = lSubs[i]; Debug.Assert(pi.Position == s.Position); Debug.Assert(pi.Length >= 1); List <QePatternInstance> lNewPath = new List <QePatternInstance>(s.Path.Count + 1); lNewPath.AddRange(s.Path); lNewPath.Add(pi); Debug.Assert(lNewPath.Capacity == (s.Path.Count + 1)); QePathState sNew = new QePathState(s.Position + pi.Length, lNewPath); sRec.Push(sNew); } } } return((uint)Math.Ceiling(dblMinCost)); }