Chromium Code Reviews| 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 "chrome/browser/signin/local_auth.h" | 5 #include "chrome/browser/signin/local_auth.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 | 24 |
| 25 // WARNING: Changing these values will make it impossible to do off-line | 25 // WARNING: Changing these values will make it impossible to do off-line |
| 26 // authentication until the next successful on-line authentication. To change | 26 // authentication until the next successful on-line authentication. To change |
| 27 // these safely, change the "encoding" version below and make verification | 27 // these safely, change the "encoding" version below and make verification |
| 28 // handle multiple values. | 28 // handle multiple values. |
| 29 const char kHash1Encoding = '1'; | 29 const char kHash1Encoding = '1'; |
| 30 const unsigned kHash1Bits = 256; | 30 const unsigned kHash1Bits = 256; |
| 31 const unsigned kHash1Bytes = kHash1Bits / 8; | 31 const unsigned kHash1Bytes = kHash1Bits / 8; |
| 32 const unsigned kHash1IterationCount = 100000; | 32 const unsigned kHash1IterationCount = 100000; |
| 33 | 33 |
| 34 // Store 13 bits to provide pin-like security (8192 possible values), without | |
| 35 // providing a complete oracle for the user's GAIA password. | |
| 36 const unsigned kHash1StoredBits = 13; | |
|
bcwhite
2015/01/21 21:04:40
"Hash2" This is storage method #2. Use the same
Mike Lerman
2015/01/27 21:02:26
Done.
| |
| 37 const unsigned kHash1StoredBytes = (kHash1StoredBits + 7) / 8; | |
| 38 | |
| 34 std::string CreateSecurePasswordHash(const std::string& salt, | 39 std::string CreateSecurePasswordHash(const std::string& salt, |
| 35 const std::string& password, | 40 const std::string& password, |
| 36 char encoding) { | 41 char encoding) { |
| 37 DCHECK_EQ(kHash1Bytes, salt.length()); | 42 DCHECK_EQ(kHash1Bytes, salt.length()); |
| 38 DCHECK_EQ(kHash1Encoding, encoding); // Currently support only one method. | 43 DCHECK_EQ(kHash1Encoding, encoding); // Currently support only one method. |
| 39 | 44 |
| 40 base::Time start_time = base::Time::Now(); | 45 base::Time start_time = base::Time::Now(); |
| 41 | 46 |
| 42 // Library call to create secure password hash as SymmetricKey (uses PBKDF2). | 47 // Library call to create secure password hash as SymmetricKey (uses PBKDF2). |
| 43 scoped_ptr<crypto::SymmetricKey> password_key( | 48 scoped_ptr<crypto::SymmetricKey> password_key( |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 // Salt should be random data, as long as the hash length, and different with | 129 // Salt should be random data, as long as the hash length, and different with |
| 125 // every save. | 130 // every save. |
| 126 std::string salt_str; | 131 std::string salt_str; |
| 127 crypto::RandBytes(WriteInto(&salt_str, kHash1Bytes + 1), kHash1Bytes); | 132 crypto::RandBytes(WriteInto(&salt_str, kHash1Bytes + 1), kHash1Bytes); |
| 128 DCHECK_EQ(kHash1Bytes, salt_str.length()); | 133 DCHECK_EQ(kHash1Bytes, salt_str.length()); |
| 129 | 134 |
| 130 // Perform secure hash of password for storage. | 135 // Perform secure hash of password for storage. |
| 131 std::string password_hash = CreateSecurePasswordHash( | 136 std::string password_hash = CreateSecurePasswordHash( |
| 132 salt_str, password, kHash1Encoding); | 137 salt_str, password, kHash1Encoding); |
| 133 DCHECK_EQ(kHash1Bytes, password_hash.length()); | 138 DCHECK_EQ(kHash1Bytes, password_hash.length()); |
| 139 password_hash = TruncateStringByBits(password_hash, kHash1StoredBits); | |
| 134 | 140 |
| 135 // Group all fields into a single record for storage; | 141 // Group all fields into a single record for storage; |
| 136 std::string record; | 142 std::string record; |
| 137 record.append(salt_str); | 143 record.append(salt_str); |
| 138 record.append(password_hash); | 144 record.append(password_hash); |
| 139 | 145 |
| 140 // Encode it and store it. | 146 // Encode it and store it. |
| 141 std::string encoded = EncodePasswordHashRecord(record, kHash1Encoding); | 147 std::string encoded = EncodePasswordHashRecord(record, kHash1Encoding); |
| 142 ProfileInfoCache& info = | 148 ProfileInfoCache& info = |
| 143 g_browser_process->profile_manager()->GetProfileInfoCache(); | 149 g_browser_process->profile_manager()->GetProfileInfoCache(); |
| 144 info.SetLocalAuthCredentialsOfProfileAtIndex(info_index, encoded); | 150 info.SetLocalAuthCredentialsOfProfileAtIndex(info_index, encoded); |
| 145 } | 151 } |
| 146 | 152 |
| 147 void SetLocalAuthCredentials(const Profile* profile, | 153 void SetLocalAuthCredentials(const Profile* profile, |
| 148 const std::string& password) { | 154 const std::string& password) { |
| 149 SetLocalAuthCredentials(GetProfileInfoIndexOfProfile(profile), password); | 155 SetLocalAuthCredentials(GetProfileInfoIndexOfProfile(profile), password); |
| 150 } | 156 } |
| 151 | 157 |
| 152 bool ValidateLocalAuthCredentials(size_t info_index, | 158 bool ValidateLocalAuthCredentials(size_t info_index, |
| 153 const std::string& password) { | 159 const std::string& password) { |
| 154 if (info_index == std::string::npos) { | 160 if (info_index == std::string::npos) { |
| 155 NOTREACHED(); | 161 NOTREACHED(); |
| 156 return false; | 162 return false; |
| 157 } | 163 } |
| 158 | 164 |
| 159 std::string record; | 165 std::string record; |
| 160 char encoding; | 166 char encoding; |
| 167 bool update_stored_credential = false; | |
| 161 | 168 |
| 162 ProfileInfoCache& info = | 169 ProfileInfoCache& info = |
| 163 g_browser_process->profile_manager()->GetProfileInfoCache(); | 170 g_browser_process->profile_manager()->GetProfileInfoCache(); |
| 164 | 171 |
| 165 std::string encodedhash = | 172 std::string encodedhash = |
| 166 info.GetLocalAuthCredentialsOfProfileAtIndex(info_index); | 173 info.GetLocalAuthCredentialsOfProfileAtIndex(info_index); |
| 167 if (encodedhash.length() == 0 && password.length() == 0) | 174 if (encodedhash.length() == 0 && password.length() == 0) |
| 168 return true; | 175 return true; |
| 169 if (!DecodePasswordHashRecord(encodedhash, &record, &encoding)) | 176 if (!DecodePasswordHashRecord(encodedhash, &record, &encoding)) |
| 170 return false; | 177 return false; |
| 171 | 178 |
| 172 std::string password_hash; | 179 std::string password_hash; |
| 173 const char* password_saved; | 180 const char* password_saved; |
| 174 const char* password_check; | 181 const char* password_check; |
| 175 size_t password_length; | 182 size_t password_length; |
| 176 | 183 |
| 177 if (encoding == '1') { | 184 if (encoding == '1') { |
|
bcwhite
2015/01/21 21:04:40
The handling of v1 encoding should never change.
Mike Lerman
2015/01/27 21:02:26
Done.
| |
| 178 // Validate correct length; extract salt and password hash. | 185 // Extract salt. |
| 179 if (record.length() != 2 * kHash1Bytes) | |
| 180 return false; | |
| 181 std::string salt_str(record.data(), kHash1Bytes); | 186 std::string salt_str(record.data(), kHash1Bytes); |
| 182 password_saved = record.data() + kHash1Bytes; | 187 password_saved = record.data() + kHash1Bytes; |
| 183 password_hash = CreateSecurePasswordHash(salt_str, password, encoding); | 188 // Extract password hash. |
| 189 if (record.length() == 2 * kHash1Bytes) { | |
| 190 password_hash = CreateSecurePasswordHash(salt_str, password, encoding); | |
| 191 password_length = kHash1Bytes; | |
| 192 update_stored_credential = true; | |
| 193 } else if (record.length() == kHash1Bytes + kHash1StoredBytes) { | |
| 194 password_hash = CreateSecurePasswordHash(salt_str, password, encoding); | |
|
jww
2015/01/22 21:48:18
Given bcwhite's comment about never changing the h
bcwhite
2015/01/23 01:28:38
I didn't pass an encoding version to "create" beca
jww
2015/01/26 18:31:51
CreateSecurePasswordHash() is used to validate the
bcwhite
2015/01/27 18:02:25
I guess I always imagined that a new method would
Mike Lerman
2015/01/27 21:02:26
Ok, CreateSecurePasswordHash will call Truncate.
| |
| 195 password_hash = TruncateStringByBits(password_hash, kHash1StoredBits); | |
| 196 password_length = kHash1StoredBytes; | |
| 197 } else { | |
| 198 // Unknown record length. | |
| 199 return false; | |
| 200 } | |
| 184 password_check = password_hash.data(); | 201 password_check = password_hash.data(); |
| 185 password_length = kHash1Bytes; | |
| 186 } else { | 202 } else { |
| 187 // unknown encoding | 203 // Unknown encoding. |
| 188 return false; | 204 return false; |
| 189 } | 205 } |
| 190 | 206 |
| 191 return crypto::SecureMemEqual(password_saved, password_check, | 207 bool passwords_match = crypto::SecureMemEqual( |
| 192 password_length); | 208 password_saved, password_check, password_length); |
| 209 if (passwords_match && update_stored_credential) | |
| 210 SetLocalAuthCredentials(info_index, password); | |
| 211 return passwords_match; | |
| 193 } | 212 } |
| 194 | 213 |
| 195 bool ValidateLocalAuthCredentials(const Profile* profile, | 214 bool ValidateLocalAuthCredentials(const Profile* profile, |
| 196 const std::string& password) { | 215 const std::string& password) { |
| 197 return ValidateLocalAuthCredentials(GetProfileInfoIndexOfProfile(profile), | 216 return ValidateLocalAuthCredentials(GetProfileInfoIndexOfProfile(profile), |
| 198 password); | 217 password); |
| 199 } | 218 } |
| 200 | 219 |
| 220 std::string TruncateStringByBits(const std::string& str, | |
|
bcwhite
2015/01/21 21:04:40
Rest of the file calls upward so put this function
Mike Lerman
2015/01/27 21:02:26
Done.
| |
| 221 const size_t len_bits) { | |
| 222 if (len_bits % 8 == 0) | |
| 223 return str.substr(0, len_bits / 8); | |
| 224 | |
| 225 // The initial truncation copies whole bytes | |
| 226 int number_bytes = (len_bits + 7) / 8; | |
| 227 std::string truncated_string = str.substr(0, number_bytes); | |
| 228 | |
| 229 // Keep the prescribed number of bits from the last byte. | |
| 230 unsigned last_char_bitmask = pow(2, len_bits % 8) - 1; | |
|
bcwhite
2015/01/21 21:04:40
pow(2, len_bits % 8) == (1 << (len_bits % 8))
Mike Lerman
2015/01/27 21:02:26
Done.
| |
| 231 truncated_string[number_bytes - 1] &= last_char_bitmask; | |
| 232 return truncated_string; | |
| 233 } | |
| 234 | |
| 201 } // namespace chrome | 235 } // namespace chrome |
| OLD | NEW |