local_auth.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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 3// found in the LICENSE file. 4 5#include "chrome/browser/signin/local_auth.h" 6 7#include "base/base64.h" 8#include "base/logging.h" 9#include "base/memory/scoped_ptr.h" 10#include "base/metrics/histogram.h" 11#include "base/prefs/pref_service.h" 12#include "base/strings/string_util.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/common/pref_names.h" 15#include "components/user_prefs/pref_registry_syncable.h" 16#include "components/webdata/encryptor/encryptor.h" 17#include "crypto/random.h" 18#include "crypto/secure_util.h" 19#include "crypto/symmetric_key.h" 20 21namespace { 22 23// WARNING: Changing these values will make it impossible to do off-line 24// authentication until the next successful on-line authentication. To change 25// these safely, change the "encoding" version below and make verification 26// handle multiple values. 27const char kHash1Encoding = '1'; 28const unsigned kHash1Bits = 256; 29const unsigned kHash1Bytes = kHash1Bits / 8; 30const unsigned kHash1IterationCount = 100000; 31 32std::string CreateSecurePasswordHash(const std::string& salt, 33 const std::string& password, 34 char encoding) { 35 DCHECK_EQ(kHash1Bytes, salt.length()); 36 DCHECK_EQ(kHash1Encoding, encoding); // Currently support only one method. 37 38 base::Time start_time = base::Time::Now(); 39 40 // Library call to create secure password hash as SymmetricKey (uses PBKDF2). 41 scoped_ptr<crypto::SymmetricKey> password_key( 42 crypto::SymmetricKey::DeriveKeyFromPassword( 43 crypto::SymmetricKey::AES, 44 password, salt, 45 kHash1IterationCount, kHash1Bits)); 46 std::string password_hash; 47 const bool success = password_key->GetRawKey(&password_hash); 48 DCHECK(success); 49 DCHECK_EQ(kHash1Bytes, password_hash.length()); 50 51 UMA_HISTOGRAM_TIMES("PasswordHash.CreateTime", 52 base::Time::Now() - start_time); 53 54 return password_hash; 55} 56 57std::string EncodePasswordHashRecord(const std::string& record, 58 char encoding) { 59 DCHECK_EQ(kHash1Encoding, encoding); // Currently support only one method. 60 61 // Encrypt the hash using the OS account-password protection (if available). 62 std::string encoded; 63 const bool success = Encryptor::EncryptString(record, &encoded); 64 DCHECK(success); 65 66 // Convert binary record to text for preference database. 67 std::string encoded64; 68 base::Base64Encode(encoded, &encoded64); 69 70 // Stuff the "encoding" value into the first byte. 71 encoded64.insert(0, &encoding, sizeof(encoding)); 72 73 return encoded64; 74} 75 76bool DecodePasswordHashRecord(const std::string& encoded, 77 std::string* decoded, 78 char* encoding) { 79 // Extract the "encoding" value from the first byte and validate. 80 if (encoded.length() < 1) 81 return false; 82 *encoding = encoded[0]; 83 if (*encoding != kHash1Encoding) 84 return false; 85 86 // Stored record is base64; convert to binary. 87 std::string unbase64; 88 if (!base::Base64Decode(encoded.substr(1), &unbase64)) 89 return false; 90 91 // Decrypt the record using the OS account-password protection (if available). 92 return Encryptor::DecryptString(unbase64, decoded); 93} 94 95} // namespace 96 97namespace chrome { 98 99void RegisterLocalAuthPrefs(user_prefs::PrefRegistrySyncable* registry) { 100 registry->RegisterStringPref( 101 prefs::kGoogleServicesPasswordHash, 102 std::string(), 103 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 104} 105 106void SetLocalAuthCredentials(Profile* profile, 107 const std::string& username, 108 const std::string& password) { 109 DCHECK(profile); 110 DCHECK(username.length()); 111 DCHECK(password.length()); 112 113 // Salt should be random data, as long as the hash length, and different with 114 // every save. 115 std::string salt_str; 116 crypto::RandBytes(WriteInto(&salt_str, kHash1Bytes + 1), kHash1Bytes); 117 DCHECK_EQ(kHash1Bytes, salt_str.length()); 118 119 // Perform secure hash of password for storage. 120 std::string password_hash = CreateSecurePasswordHash( 121 salt_str, password, kHash1Encoding); 122 DCHECK_EQ(kHash1Bytes, password_hash.length()); 123 124 // Group all fields into a single record for storage; 125 std::string record; 126 record.append(salt_str); 127 record.append(password_hash); 128 record.append(username); 129 130 // Encode it and store it. 131 std::string encoded = EncodePasswordHashRecord(record, kHash1Encoding); 132 profile->GetPrefs()->SetString(prefs::kGoogleServicesPasswordHash, 133 encoded); 134} 135 136bool ValidateLocalAuthCredentials(Profile* profile, 137 const std::string& username, 138 const std::string& password) { 139 DCHECK(profile); 140 DCHECK(username.length()); 141 DCHECK(password.length()); 142 143 const size_t name_length = username.length(); 144 std::string record; 145 char encoding; 146 147 if (!profile->GetPrefs()->HasPrefPath(prefs::kGoogleServicesPasswordHash)) 148 return false; 149 std::string encodedhash = profile->GetPrefs()->GetString( 150 prefs::kGoogleServicesPasswordHash); 151 if (!DecodePasswordHashRecord(encodedhash, &record, &encoding)) 152 return false; 153 154 std::string password_hash; 155 const char* password_saved; 156 const char* password_check; 157 size_t password_length; 158 159 if (encoding == '1') { 160 // Validate correct length and username; extract salt and password hash. 161 if (record.length() != 2 * kHash1Bytes + name_length) 162 return false; 163 if (record.compare(2 * kHash1Bytes, name_length, username) != 0) 164 return false; 165 std::string salt_str(record.data(), kHash1Bytes); 166 password_saved = record.data() + kHash1Bytes; 167 password_hash = CreateSecurePasswordHash(salt_str, password, encoding); 168 password_check = password_hash.data(); 169 password_length = kHash1Bytes; 170 } else { 171 // unknown encoding 172 return false; 173 } 174 175 return crypto::SecureMemEqual(password_saved, password_check, 176 password_length); 177} 178 179} // namespace chrome 180