/// <summary> /// Log results of analysis /// </summary> /// <param name="strength"></param> /// <param name="description"></param> /// <param name="rate"></param> /// <param name="count"></param> /// <param name="bonus"></param> /// <param name="malus"></param> /// <returns></returns> private static void AddAnalysisResult(PasswordStrength strength, string description, string rate, int count, int bonus, int malus) { var dr = new StrengthDetail { Description = description ?? "", Rate = rate ?? "", Count = count, Bonus = bonus, Malus = malus }; strength.Details.Add(dr); }
private static PasswordStrength DoCheck(Requirements req, string pwd) { const int numOfSequChars = 3; var passwordStrength = new PasswordStrength(); var forbiddenWordsShare = 0; const string alphaLower = "abcdefghijklmnopqrstuvwxyz"; const string numeric = "01234567890"; const string keyboard = "^1234567890ß´^!\"§$%&/()=?`qwertzuiopü+QWERTZUIOPÜ*asdfghjklöä#ASDFGHJKLÖÄ'<yxcvbnm,.->YXCVBNM;:_"; var sequAlphaCount = 0; var sequDigitCount = 0; var sequKeyboardCount = 0; // count digits and special characters which are embedded between characters var embeddedCount = (new Regex("[a-zA-Z][^a-zA-Z]", RegexOptions.CultureInvariant | RegexOptions.Compiled)).Matches(pwd).Count; var digitCount = (new Regex("[0-9]", RegexOptions.CultureInvariant | RegexOptions.Compiled)).Matches(pwd).Count; var lowerCount = (new Regex("[a-z]", RegexOptions.CultureInvariant | RegexOptions.Compiled)).Matches(pwd).Count; var upperCount = (new Regex("[A-Z]", RegexOptions.CultureInvariant | RegexOptions.Compiled)).Matches(pwd).Count; var whiteSpaceCount = (new Regex(@"\s", RegexOptions.CultureInvariant | RegexOptions.Compiled)).Matches(pwd).Count; var specialCount = pwd.Length - digitCount - lowerCount - upperCount; var consecutiveUpperCount = (new Regex("[A-Z]{2,}", RegexOptions.CultureInvariant | RegexOptions.Compiled)).Matches(pwd).Count; var consecutiveLowerCount = (new Regex("[a-z]{2,}", RegexOptions.CultureInvariant | RegexOptions.Compiled)).Matches(pwd).Count; var consecutiveDigitCount = (new Regex("[0-9]{2,}", RegexOptions.CultureInvariant | RegexOptions.Compiled)).Matches(pwd).Count; // Check for sequential alpha string patterns (like "abcd", forward and reverse) for (var s = 0; s < alphaLower.Length - numOfSequChars; s++) { var fwd = alphaLower.Substring(s, numOfSequChars); var rev = Reverse(fwd); if (pwd.ToLower(CultureInfo.InvariantCulture).IndexOf(fwd, StringComparison.InvariantCulture) != -1) { passwordStrength.SequCharsFound.Add(fwd); sequAlphaCount++; } if (pwd.ToLower(CultureInfo.InvariantCulture).IndexOf(rev, StringComparison.InvariantCulture) != -1) { passwordStrength.SequCharsFound.Add(rev); sequAlphaCount++; } } // Check for sequential numeric string patterns (like "1234" forward and reverse) for (var s = 0; s < numeric.Length - numOfSequChars; s++) { var fwd = numeric.Substring(s, numOfSequChars); var rev = Reverse(fwd); if (pwd.ToLower(CultureInfo.InvariantCulture).IndexOf(fwd, StringComparison.InvariantCulture) != -1) { passwordStrength.SequCharsFound.Add(fwd); sequDigitCount++; } if (pwd.ToLower(CultureInfo.InvariantCulture).IndexOf(rev, StringComparison.InvariantCulture) != -1) { passwordStrength.SequCharsFound.Add(rev); sequDigitCount++; } } // Check for sequential German keyboard string patterns (like "asdfghjkl" forward and reverse) for (var s = 0; s < keyboard.Length - numOfSequChars; s++) { var fwd = keyboard.Substring(s, numOfSequChars); var rev = Reverse(fwd); if (pwd.ToLower(CultureInfo.InvariantCulture).IndexOf(fwd, StringComparison.InvariantCulture) != -1) { passwordStrength.SequCharsFound.Add(fwd); sequKeyboardCount++; } if (pwd.ToLower(CultureInfo.InvariantCulture).IndexOf(rev, StringComparison.InvariantCulture) != -1) { passwordStrength.SequCharsFound.Add(rev); sequKeyboardCount++; } } // Check for usage of forbidden words (forward and reverse) foreach (var word in req.ForbiddenWords) { var fwd = word.ToLower(CultureInfo.InvariantCulture); var rev = Reverse(word).ToLower(CultureInfo.InvariantCulture); if (pwd.ToLower(CultureInfo.InvariantCulture).IndexOf(fwd, StringComparison.InvariantCulture) != -1) { forbiddenWordsShare += (int)100f / pwd.Length * word.Length; passwordStrength.MissedRequirements.ForbiddenWords.Add(fwd); } if (pwd.ToLower().IndexOf(rev, StringComparison.InvariantCulture) != -1) { forbiddenWordsShare += (int)100f / pwd.Length * word.Length; passwordStrength.MissedRequirements.ForbiddenWords.Add(rev); } } // Score += 4 * Password Length AddAnalysisResult(passwordStrength, "Password Length", "(n*4)", pwd.Length, pwd.Length * 4, 0); // if we have uppercase letters Score += (number of uppercase letters *2) if (upperCount > 0) { AddAnalysisResult(passwordStrength, "Uppercase Letters", "+((len-n)*2)", upperCount, ((pwd.Length - upperCount) * 2), 0); } else { AddAnalysisResult(passwordStrength, "Uppercase Letters", "+((len-n)*2)", upperCount, 0, 0); } // if we have lowercase letters Score += (number of lowercase letters *2) if (lowerCount > 0) { AddAnalysisResult(passwordStrength, "Lowercase Letters", "+((len-n)*2)", lowerCount, ((pwd.Length - lowerCount) * 2), 0); } else { AddAnalysisResult(passwordStrength, "Lowercase Letters", "+((len-n)*2)", lowerCount, 0, 0); } // Score += (Number of digits * 4) AddAnalysisResult(passwordStrength, "Numbers", "+(n*4)", digitCount, (digitCount * 4), 0); // Score += (Number of Symbols * 6) AddAnalysisResult(passwordStrength, "Symbols", "+(n*6)", specialCount, (specialCount * 6), 0); // Score += (Number of embedded digits or symbols in middle of password *2) AddAnalysisResult(passwordStrength, "Embedded Numbers or Symbols", "+(n*2)", embeddedCount, (embeddedCount * 2), 0); var requirements = (req[RequiredCriteria.MinLength] >= 0 ? 1 : 0) + (req[RequiredCriteria.Uppercase] > 0 ? 1 : 0) + (req[RequiredCriteria.Lowercase] > 0 ? 1 : 0) + (req[RequiredCriteria.Digits] > 0 ? 1 : 0) + (req[RequiredCriteria.Special] > 0 ? 1 : 0); if (pwd.Length >= req[RequiredCriteria.MinLength]) { passwordStrength.Score += pwd.Length; AddAnalysisResult(passwordStrength, "Requirement. Length > " + req[RequiredCriteria.MinLength], "+(n)", pwd.Length, pwd.Length, 0); } else { passwordStrength.MissedRequirements.Add(RequiredCriteria.MinLength, pwd.Length); } if (req[RequiredCriteria.Uppercase] > 0) { if (upperCount >= req[RequiredCriteria.Uppercase]) { AddAnalysisResult(passwordStrength, "Requirement. Uppercase", "+(n)", upperCount, upperCount, 0); } else { passwordStrength.MissedRequirements.Add(RequiredCriteria.Uppercase, upperCount); } } if (req[RequiredCriteria.Lowercase] > 0) { if (lowerCount >= req[RequiredCriteria.Lowercase]) { AddAnalysisResult(passwordStrength, "Requirement. Lowercase", "+(n)", lowerCount, lowerCount, 0); } else { passwordStrength.MissedRequirements.Add(RequiredCriteria.Lowercase, lowerCount); } } if (req[RequiredCriteria.Digits] > 0) { if (digitCount >= req[RequiredCriteria.Digits]) { AddAnalysisResult(passwordStrength, "Requirement. Digit", "+(n)", digitCount, digitCount, 0); } else { passwordStrength.MissedRequirements.Add(RequiredCriteria.Digits, digitCount); } } if (req[RequiredCriteria.Special] > 0) { if (specialCount >= req[RequiredCriteria.Special]) { AddAnalysisResult(passwordStrength, "Requirement. Special", "+(2n)", specialCount, specialCount * 2, 0); } else { passwordStrength.MissedRequirements.Add(RequiredCriteria.Special, specialCount); } } // special treatment, because this is not a quality requirement if (req.NoWhitespaceAllowed && whiteSpaceCount > 0) { AddAnalysisResult(passwordStrength, "Requirement. No Control or Whitespace", string.Empty, whiteSpaceCount, 0, 0); passwordStrength.MissedRequirements.NoWhitespaceAllowed = false; } // If we have more than 3 requirments then if (requirements > 3) { AddAnalysisResult(passwordStrength, "3 or more requirements", "+(n*2)", requirements, (requirements * 2), 0); } else { AddAnalysisResult(passwordStrength, "Less than 3 requirements", "-(5-n)*2", requirements, 0, (5 - requirements) * 2); } // // Deductions // // If only letters then score -= password length if (digitCount == 0 && specialCount == 0) { AddAnalysisResult(passwordStrength, "Letters only", "-n", pwd.Length, 0, pwd.Length); } else { AddAnalysisResult(passwordStrength, "Letters only", "-n", 0, 0, 0); } // Check for number of distinct characters used in the password var distinctChars = pwd.ToCharArray().Distinct().Count(); AddAnalysisResult(passwordStrength, "Distinct Characters", "-(% of not distinct chars)", distinctChars, 0, (100 - (int)(100f / pwd.Length * distinctChars)) / 4); // If only digits then score -= password length if (digitCount == pwd.Length) { AddAnalysisResult(passwordStrength, "Numbers only", "-n", pwd.Length, 0, pwd.Length); } else { AddAnalysisResult(passwordStrength, "Numbers only", "-n", 0, 0, 0); } // If Consecutive uppercase letters then score -= (consecutiveUpperCount * 2); AddAnalysisResult(passwordStrength, "Consecutive Uppercase Letters", "-(n*2)", consecutiveUpperCount, 0, consecutiveUpperCount * 2); // If Consecutive lowercase letters then score -= (consecutiveLowerCount * 2); AddAnalysisResult(passwordStrength, "Consecutive Lowercase Letters", "-(n*2)", consecutiveLowerCount, 0, consecutiveLowerCount * 2); // If Consecutive digits used then score -= (consecutiveDigitCount * 2); AddAnalysisResult(passwordStrength, "Consecutive Numbers", "-(n*2)", consecutiveDigitCount, 0, consecutiveDigitCount * 2); // If password contains sequence of letters then score -= (100 / pwd.Length * sequAlphaCount) AddAnalysisResult(passwordStrength, "Sequential Letters (3+)", "-(% of pw length)", sequAlphaCount, 0, (int)100f / pwd.Length * sequAlphaCount); // If password contains sequence of digits then score -= (100 / pwd.Length * sequDigitCount) AddAnalysisResult(passwordStrength, "Sequential Numbers (3+)", "-(% of pw length)", sequDigitCount, 0, (int)100f / pwd.Length * sequDigitCount); // If password contains sequence of keyboard keys then score -= (100 / pwd.Length * sequDigitCount) AddAnalysisResult(passwordStrength, "Sequential Keyboard Keys (3+)", "-(% of pw length)", sequDigitCount, 0, (int)100f / pwd.Length * sequKeyboardCount); // If password contains sequence of digits then score -= forbiddenWordsShare AddAnalysisResult(passwordStrength, "Forbidden Words", "-(% of words of pw length)", forbiddenWordsShare, 0, forbiddenWordsShare); passwordStrength.Score = passwordStrength.Details.Sum(d => d.Bonus) - passwordStrength.Details.Sum(d => d.Malus); if (passwordStrength.Score > 100) { passwordStrength.Score = 100; } else if (passwordStrength.Score < 0) { passwordStrength.Score = 0; } if (passwordStrength.Score < 20) { passwordStrength.Rate = StrengthRate.Unsafe; } else if (passwordStrength.Score >= 20 && passwordStrength.Score < 40) { passwordStrength.Rate = StrengthRate.Weak; } else if (passwordStrength.Score >= 40 && passwordStrength.Score < 60) { passwordStrength.Rate = StrengthRate.Fair; } else if (passwordStrength.Score >= 60 && passwordStrength.Score < 80) { passwordStrength.Rate = StrengthRate.Strong; } else if (passwordStrength.Score >= 80) { passwordStrength.Rate = StrengthRate.Secure; } return(passwordStrength); }