diff --git a/common/account_utils/src/lib.rs b/common/account_utils/src/lib.rs index d74ed71ed8..f969db7020 100644 --- a/common/account_utils/src/lib.rs +++ b/common/account_utils/src/lib.rs @@ -140,11 +140,17 @@ fn trim_newline(s: &mut String) { } } +/// According to unicode, every byte that starts with 0b10xxxxxx continues encoding of character +/// Therefore the number of characters equals number of bytes minus number of 0b10xxxxxx bytes +fn count_unicode_characters(bits: &[u8]) -> usize { + bits.iter().filter(|bit| *bit >> 6 != 2).count() +} + /// Takes a string password and checks that it meets minimum requirements. /// /// The current minimum password requirement is a 12 character length character length. pub fn is_password_sufficiently_complex(password: &[u8]) -> Result<(), String> { - if password.len() >= MINIMUM_PASSWORD_LEN { + if count_unicode_characters(password) >= MINIMUM_PASSWORD_LEN { Ok(()) } else { Err(format!( @@ -192,8 +198,7 @@ impl AsRef<[u8]> for ZeroizeString { #[cfg(test)] mod test { - use super::is_password_sufficiently_complex; - use super::strip_off_newlines; + use super::*; #[test] fn test_strip_off() { @@ -244,4 +249,18 @@ mod test { fn test_password_too_short() { is_password_sufficiently_complex(b"TestPass").unwrap(); } + + #[test] + fn unicode_characters() { + assert_eq!(count_unicode_characters(b""), 0); + assert_eq!(count_unicode_characters("🐱".to_string().as_bytes()), 1); + assert_eq!(count_unicode_characters("🐱🐱".to_string().as_bytes()), 2); + + assert_eq!(count_unicode_characters(b"cats"), 4); + assert_eq!(count_unicode_characters("cats🐱".to_string().as_bytes()), 5); + assert_eq!( + count_unicode_characters("cats🐱🐱".to_string().as_bytes()), + 6 + ); + } }