private void CalculateEntropyForMatch(DictionaryMatch match) { match.BaseEntropy = Math.Log(match.Rank, 2); match.UppercaseEntropy = PasswordScoring.CalculateUppercaseEntropy(match.Token); match.Entropy = match.BaseEntropy + match.UppercaseEntropy; }
// 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> /// 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()); }