OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/autofill/core/browser/password_generator.h" | 5 #include "components/autofill/core/browser/password_generator.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
11 #include "base/logging.h" | |
12 #include "base/rand_util.h" | 11 #include "base/rand_util.h" |
| 12 #include "base/strings/string_util.h" |
| 13 #include "third_party/fips181/fips181.h" |
13 | 14 |
14 const int kMinUpper = 65; // First upper case letter 'A' | 15 const int kMinUpper = 65; // First upper case letter 'A' |
15 const int kMaxUpper = 90; // Last upper case letter 'Z' | 16 const int kMaxUpper = 90; // Last upper case letter 'Z' |
16 const int kMinLower = 97; // First lower case letter 'a' | 17 const int kMinLower = 97; // First lower case letter 'a' |
17 const int kMaxLower = 122; // Last lower case letter 'z' | 18 const int kMaxLower = 122; // Last lower case letter 'z' |
18 const int kMinDigit = 48; // First digit '0' | 19 const int kMinDigit = 48; // First digit '0' |
19 const int kMaxDigit = 57; // Last digit '9' | 20 const int kMaxDigit = 57; // Last digit '9' |
20 // Copy of the other printable symbols from the ASCII table since they are | |
21 // disjointed. | |
22 const char kOtherSymbols[] = | |
23 {'!', '\"', '#', '$', '%', '&', '\'', '(', | |
24 ')', '*', '+', ',', '-', '.', '/', ':', | |
25 ';', '<', '=', '>', '?', '@', '[', '\\', | |
26 ']', '^', '_', '`', '{', '|', '}', '~'}; | |
27 const int kMinPasswordLength = 4; | 21 const int kMinPasswordLength = 4; |
28 const int kMaxPasswordLength = 15; | 22 const int kMaxPasswordLength = 15; |
29 | 23 |
30 namespace { | 24 namespace { |
31 | 25 |
32 // A helper function to get the length of the generated password from | 26 // A helper function to get the length of the generated password from |
33 // |max_length| retrieved from input password field. | 27 // |max_length| retrieved from input password field. |
34 int GetLengthFromHint(int max_length, int default_length) { | 28 int GetLengthFromHint(int max_length, int default_length) { |
35 if (max_length >= kMinPasswordLength && max_length <= kMaxPasswordLength) | 29 if (max_length >= kMinPasswordLength && max_length <= kMaxPasswordLength) |
36 return max_length; | 30 return max_length; |
37 else | 31 else |
38 return default_length; | 32 return default_length; |
39 } | 33 } |
40 | 34 |
41 void InitializeAlphaNumericCharacters(std::vector<char>* characters) { | 35 // We want the password to have uppercase, lowercase, and at least one number. |
42 for (int i = kMinDigit; i <= kMaxDigit; ++i) | 36 bool VerifyPassword(const std::string& password) { |
43 characters->push_back(static_cast<char>(i)); | 37 int num_lower_case = 0; |
44 for (int i = kMinUpper; i <= kMaxUpper; ++i) | 38 int num_upper_case = 0; |
45 characters->push_back(static_cast<char>(i)); | 39 int num_digits = 0; |
46 for (int i = kMinLower; i <= kMaxLower; ++i) | 40 |
47 characters->push_back(static_cast<char>(i)); | 41 for (size_t i = 0; i < password.size(); ++i) { |
| 42 if (password[i] >= kMinUpper && password[i] <= kMaxUpper) |
| 43 ++num_upper_case; |
| 44 if (password[i] >= kMinLower && password[i] <= kMaxLower) |
| 45 ++num_lower_case; |
| 46 if (password[i] >= kMinDigit && password[i] <= kMaxDigit) |
| 47 ++num_digits; |
| 48 } |
| 49 |
| 50 return num_lower_case && num_upper_case && num_digits; |
48 } | 51 } |
49 | 52 |
50 // Classic algorithm to randomly select |num_select| elements out of | 53 // Make sure that there is at least one upper case and one number in the |
51 // |num_total| elements. One description can be found at: | 54 // password. Assume that there already exists a lower case letter as it's the |
52 // "http://stackoverflow.com/questions/48087/select-a-random-n-elements-from-lis
tt-in-c-sharp/48089#48089" | 55 // default from gen_pron_pass. |
53 void GetRandomSelection(int num_to_select, | 56 void ForceFixPassword(std::string* password) { |
54 int num_total, | 57 for (std::string::iterator iter = password->begin(); |
55 std::vector<int>* selections) { | 58 iter != password->end(); ++iter) { |
56 DCHECK_GE(num_total, num_to_select); | 59 if (islower(*iter)) { |
57 int num_left = num_total; | 60 *iter = base::ToUpperASCII(*iter); |
58 int num_needed = num_to_select; | 61 break; |
59 for (int i = 0; i < num_total && num_needed > 0; ++i) { | |
60 // we have probability = |num_needed| / |num_left| to select | |
61 // this position. | |
62 int probability = base::RandInt(0, num_left - 1); | |
63 if (probability < num_needed) { | |
64 selections->push_back(i); | |
65 --num_needed; | |
66 } | 62 } |
67 --num_left; | |
68 } | 63 } |
69 DCHECK_EQ(num_to_select, static_cast<int>(selections->size())); | 64 for (std::string::reverse_iterator iter = password->rbegin(); |
| 65 iter != password->rend(); ++iter) { |
| 66 if (islower(*iter)) { |
| 67 *iter = base::RandInt(kMinDigit, kMaxDigit); |
| 68 break; |
| 69 } |
| 70 } |
70 } | 71 } |
71 | 72 |
72 } // namespace | 73 } // namespace |
73 | 74 |
74 namespace autofill { | 75 namespace autofill { |
75 | 76 |
76 const int PasswordGenerator::kDefaultPasswordLength = 12; | 77 const int PasswordGenerator::kDefaultPasswordLength = 12; |
77 | 78 |
78 PasswordGenerator::PasswordGenerator(int max_length) | 79 PasswordGenerator::PasswordGenerator(int max_length) |
79 : password_length_(GetLengthFromHint(max_length, kDefaultPasswordLength)) {} | 80 : password_length_(GetLengthFromHint(max_length, kDefaultPasswordLength)) {} |
80 PasswordGenerator::~PasswordGenerator() {} | 81 PasswordGenerator::~PasswordGenerator() {} |
81 | 82 |
82 std::string PasswordGenerator::Generate() const { | 83 std::string PasswordGenerator::Generate() const { |
83 std::string ret; | 84 char password[255]; |
84 CR_DEFINE_STATIC_LOCAL(std::vector<char>, alphanumeric_characters, ()); | 85 char unused_hypenated_password[255]; |
85 if (alphanumeric_characters.empty()) | 86 // Generate passwords that have numbers and upper and lower case letters. |
86 InitializeAlphaNumericCharacters(&alphanumeric_characters); | 87 // No special characters included for now. |
| 88 unsigned int mode = S_NB | S_CL | S_SL; |
87 | 89 |
88 // First, randomly select 4 positions to hold one upper case letter, | 90 // gen_pron_pass() doesn't guarantee that it includes all of the type given |
89 // one lower case letter, one digit, and one other symbol respectively, | 91 // in mode, so regenerate a few times if neccessary. |
90 // to make sure at least one of each category of characters will be | 92 // TODO(gcasto): Is it worth regenerating at all? |
91 // included in the password. | 93 for (int i = 0; i < 10; ++i) { |
92 std::vector<int> positions; | 94 gen_pron_pass(password, unused_hypenated_password, |
93 GetRandomSelection(4, password_length_, &positions); | 95 password_length_, password_length_, mode); |
| 96 if (VerifyPassword(password)) |
| 97 break; |
| 98 } |
94 | 99 |
95 // To enhance the strengh of the password, we random suffle the positions so | 100 // If the password still isn't conforming after a few iterations, force it |
96 // that the 4 catagories can be put at a random position in it. | 101 // to be so. This may change a syllable in the password. |
97 std::random_shuffle(positions.begin(), positions.end()); | 102 std::string str_password(password); |
98 | 103 if (!VerifyPassword(str_password)) { |
99 // Next, generate each character of the password. | 104 ForceFixPassword(&str_password); |
100 for (int i = 0; i < password_length_; ++i) { | |
101 if (i == positions[0]) { | |
102 // Generate random upper case letter. | |
103 ret.push_back(static_cast<char>(base::RandInt(kMinUpper, kMaxUpper))); | |
104 } else if (i == positions[1]) { | |
105 // Generate random lower case letter. | |
106 ret.push_back(static_cast<char>(base::RandInt(kMinLower, kMaxLower))); | |
107 } else if (i == positions[2]) { | |
108 // Generate random digit. | |
109 ret.push_back(static_cast<char>(base::RandInt(kMinDigit, kMaxDigit))); | |
110 } else if (i == positions[3]) { | |
111 // Generate random other symbol. | |
112 ret.push_back( | |
113 kOtherSymbols[base::RandInt(0, arraysize(kOtherSymbols) - 1)]); | |
114 } else { | |
115 // Generate random alphanumeric character. We don't use other symbols | |
116 // here as most sites don't allow a lot of non-alphanumeric characters. | |
117 ret.push_back( | |
118 alphanumeric_characters.at( | |
119 base::RandInt(0, alphanumeric_characters.size() - 1))); | |
120 } | |
121 } | 105 } |
122 return ret; | 106 return str_password; |
123 } | 107 } |
124 | 108 |
125 } // namespace autofill | 109 } // namespace autofill |
OLD | NEW |