/* KeePass Password Safe - The Open-Source Password Manager Copyright (C) 2003-2012 Dominik Reichl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using KeePassLib.Utility; namespace KeePassLib.Cryptography { /// /// A class that offers static functions to estimate the quality of /// passwords. /// public static class QualityEstimation { private enum CharSpaceBits : uint { Control = 32, Alpha = 26, Number = 10, Special = 33, High = 112 } /// /// Estimate the quality of a password. /// /// Password to check. /// Estimated bit-strength of the password. /// Thrown if the input /// parameter is null. public static uint EstimatePasswordBits(char[] vPasswordChars) { Debug.Assert(vPasswordChars != null); if(vPasswordChars == null) throw new ArgumentNullException("vPasswordChars"); bool bChLower = false, bChUpper = false, bChNumber = false; bool bChSpecial = false, bChHigh = false, bChControl = false; Dictionary vCharCounts = new Dictionary(); Dictionary vDifferences = new Dictionary(); double dblEffectiveLength = 0.0; for(int i = 0; i < vPasswordChars.Length; ++i) // Get character types { char tch = vPasswordChars[i]; if(tch < ' ') bChControl = true; else if((tch >= 'A') && (tch <= 'Z')) bChUpper = true; else if((tch >= 'a') && (tch <= 'z')) bChLower = true; else if((tch >= '0') && (tch <= '9')) bChNumber = true; else if((tch >= ' ') && (tch <= '/')) bChSpecial = true; else if((tch >= ':') && (tch <= '@')) bChSpecial = true; else if((tch >= '[') && (tch <= '`')) bChSpecial = true; else if((tch >= '{') && (tch <= '~')) bChSpecial = true; else if(tch > '~') bChHigh = true; double dblDiffFactor = 1.0; if(i >= 1) { int iDiff = (int)tch - (int)vPasswordChars[i - 1]; uint uDiffCount; if(vDifferences.TryGetValue(iDiff, out uDiffCount)) { ++uDiffCount; vDifferences[iDiff] = uDiffCount; dblDiffFactor /= (double)uDiffCount; } else vDifferences.Add(iDiff, 1); } uint uCharCount; if(vCharCounts.TryGetValue(tch, out uCharCount)) { ++uCharCount; vCharCounts[tch] = uCharCount; dblEffectiveLength += dblDiffFactor * (1.0 / (double)uCharCount); } else { vCharCounts.Add(tch, 1); dblEffectiveLength += dblDiffFactor; } } uint uCharSpace = 0; if(bChControl) uCharSpace += (uint)CharSpaceBits.Control; if(bChUpper) uCharSpace += (uint)CharSpaceBits.Alpha; if(bChLower) uCharSpace += (uint)CharSpaceBits.Alpha; if(bChNumber) uCharSpace += (uint)CharSpaceBits.Number; if(bChSpecial) uCharSpace += (uint)CharSpaceBits.Special; if(bChHigh) uCharSpace += (uint)CharSpaceBits.High; if(uCharSpace == 0) return 0; double dblBitsPerChar = Math.Log((double)uCharSpace) / Math.Log(2.0); double dblRating = dblBitsPerChar * dblEffectiveLength; #if !KeePassLibSD char[] vLowerCopy = new char[vPasswordChars.Length]; for(int ilc = 0; ilc < vLowerCopy.Length; ++ilc) vLowerCopy[ilc] = char.ToLower(vPasswordChars[ilc]); if(PopularPasswords.IsPopularPassword(vLowerCopy)) dblRating /= 8.0; Array.Clear(vLowerCopy, 0, vLowerCopy.Length); #endif return (uint)Math.Ceiling(dblRating); } /// /// Estimate the quality of a password. /// /// Password to check, UTF-8 encoded. /// Estimated bit-strength of the password. public static uint EstimatePasswordBits(byte[] pbUnprotectedUtf8) { if(pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; } char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8); uint uResult = EstimatePasswordBits(vChars); Array.Clear(vChars, 0, vChars.Length); return uResult; } } }