Search code examples
c#.netwpfpasswordbox

How can I integrate complexity verification while using a PasswordBox?


I have an application that needs to have the user create a passphrase. As such, there are various rules that need to be tested against the passphrase as it's being entered.

These rules are pretty typical-- the value must be a certain length, have one upper, one lower, include some special characters, etc.

However, since this is a WPF application using a PasswordBox control, and the resulting value is very sensitive, how can I setup a complexity comparison system that dynamically checks the passphrase as the user is typing?

To be clear, I have a list of all requirements in a text label below the passphrase creation PasswordBox element. As the user types, a validator shows which requirements are met and which ones are still needed. I also have an entropy calculator which give a typical "OK, Good, Strong, Very Strong" indicator once requirements are met. This is why I need to find a way to securely validate the value as the user is typing.

How can I accomplish this without emitting insecure .Net strings?


Solution

  • You can subscribe to PasswordChanged but don't use Password property if you care about securely storing your sensetive value. Instead, do this:

    private void OnPasswordChanged(object sender, RoutedEventArgs e) {            
        using (var pwd = ((PasswordBox) sender).SecurePassword) {
            int length = pwd.Length;
            if (length == 0) {
                // string empty, do something
                return;
            }
            bool hasSpecial = false;
            bool hasUpper = false;
            bool hasLower = false;
            bool hasDigit = false;
            // etc
            // allocate unmanaged memory and copy string there
            IntPtr ptr = Marshal.SecureStringToBSTR(pwd);
            try {
                // each char in that string is 2 bytes, not one (it's UTF-16 string)
                for (int i = 0; i < length * 2; i += 2) {
                    // so use ReadInt16 and convert resulting "short" to char
                    var ch = Convert.ToChar(Marshal.ReadInt16(ptr + i));
                    // run your checks
                    hasSpecial |= IsSpecialChar(ch);
                    hasUpper |= Char.IsUpper(ch);
                    hasLower |= Char.IsLower(ch);
                    hasDigit |= Char.IsDigit(ch);                        
                }
    
            }
            finally {
                // don't forget to zero memory to remove password from it                    
                Marshal.ZeroFreeBSTR(ptr);
            }
        }
    }
    

    That way you never build .NET string during your validation, and every trace of password is cleared from memory when you finish.