11e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
21e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
31e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// found in the LICENSE file.
41e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
51e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "chrome/browser/signin/local_auth.h"
61e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
71e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/base64.h"
81e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/logging.h"
91e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/metrics/histogram.h"
111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/prefs/pref_service.h"
121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/strings/string_util.h"
13a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "chrome/browser/browser_process.h"
141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "chrome/browser/profiles/profile_manager.h"
161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "chrome/common/pref_names.h"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "components/os_crypt/os_crypt.h"
18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "components/pref_registry/pref_registry_syncable.h"
191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "crypto/random.h"
201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "crypto/secure_util.h"
211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "crypto/symmetric_key.h"
221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)namespace {
241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// WARNING: Changing these values will make it impossible to do off-line
261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// authentication until the next successful on-line authentication.  To change
271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// these safely, change the "encoding" version below and make verification
281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// handle multiple values.
291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const char kHash1Encoding = '1';
301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const unsigned kHash1Bits = 256;
311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const unsigned kHash1Bytes = kHash1Bits / 8;
321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const unsigned kHash1IterationCount = 100000;
331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)std::string CreateSecurePasswordHash(const std::string& salt,
351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                     const std::string& password,
361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                     char encoding) {
371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK_EQ(kHash1Bytes, salt.length());
381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK_EQ(kHash1Encoding, encoding);  // Currently support only one method.
391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  base::Time start_time = base::Time::Now();
411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Library call to create secure password hash as SymmetricKey (uses PBKDF2).
431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  scoped_ptr<crypto::SymmetricKey> password_key(
441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      crypto::SymmetricKey::DeriveKeyFromPassword(
451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          crypto::SymmetricKey::AES,
461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          password, salt,
471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          kHash1IterationCount, kHash1Bits));
481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string password_hash;
491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  const bool success = password_key->GetRawKey(&password_hash);
501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK(success);
511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK_EQ(kHash1Bytes, password_hash.length());
521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  UMA_HISTOGRAM_TIMES("PasswordHash.CreateTime",
541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                      base::Time::Now() - start_time);
551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return password_hash;
571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)std::string EncodePasswordHashRecord(const std::string& record,
601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                     char encoding) {
611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK_EQ(kHash1Encoding, encoding);  // Currently support only one method.
621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Encrypt the hash using the OS account-password protection (if available).
641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string encoded;
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const bool success = OSCrypt::EncryptString(record, &encoded);
661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK(success);
671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Convert binary record to text for preference database.
691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string encoded64;
701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  base::Base64Encode(encoded, &encoded64);
711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Stuff the "encoding" value into the first byte.
731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  encoded64.insert(0, &encoding, sizeof(encoding));
741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return encoded64;
761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)bool DecodePasswordHashRecord(const std::string& encoded,
791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                              std::string* decoded,
801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                              char* encoding) {
811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Extract the "encoding" value from the first byte and validate.
821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (encoded.length() < 1)
831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return false;
841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  *encoding = encoded[0];
851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (*encoding != kHash1Encoding)
861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return false;
871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Stored record is base64; convert to binary.
891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string unbase64;
901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (!base::Base64Decode(encoded.substr(1), &unbase64))
911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return false;
921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Decrypt the record using the OS account-password protection (if available).
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return OSCrypt::DecryptString(unbase64, decoded);
951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccisize_t GetProfileInfoIndexOfProfile(const Profile* profile) {
981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(profile);
991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ProfileInfoCache& info =
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      g_browser_process->profile_manager()->GetProfileInfoCache();
1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return info.GetIndexOfProfileWithPath(profile->GetPath());
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}  // namespace
1061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)namespace chrome {
1081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void RegisterLocalAuthPrefs(user_prefs::PrefRegistrySyncable* registry) {
1101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  registry->RegisterStringPref(
1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      prefs::kGoogleServicesPasswordHash,
1121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      std::string(),
1131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
1151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
116a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void SetLocalAuthCredentials(size_t info_index,
1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                             const std::string& password) {
1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (info_index == std::string::npos) {
1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    NOTREACHED();
1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK(password.length());
1231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Salt should be random data, as long as the hash length, and different with
1251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // every save.
1261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string salt_str;
1271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  crypto::RandBytes(WriteInto(&salt_str, kHash1Bytes + 1), kHash1Bytes);
1281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK_EQ(kHash1Bytes, salt_str.length());
1291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Perform secure hash of password for storage.
1311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string password_hash = CreateSecurePasswordHash(
1321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      salt_str, password, kHash1Encoding);
1331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  DCHECK_EQ(kHash1Bytes, password_hash.length());
1341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Group all fields into a single record for storage;
1361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string record;
1371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  record.append(salt_str);
1381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  record.append(password_hash);
1391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Encode it and store it.
1411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string encoded = EncodePasswordHashRecord(record, kHash1Encoding);
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ProfileInfoCache& info =
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      g_browser_process->profile_manager()->GetProfileInfoCache();
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  info.SetLocalAuthCredentialsOfProfileAtIndex(info_index, encoded);
1451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
1461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void SetLocalAuthCredentials(const Profile* profile,
148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                             const std::string& password) {
1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  SetLocalAuthCredentials(GetProfileInfoIndexOfProfile(profile), password);
1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool ValidateLocalAuthCredentials(size_t info_index,
1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                  const std::string& password) {
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (info_index == std::string::npos) {
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    NOTREACHED();
1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string record;
1601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  char encoding;
1611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
162a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ProfileInfoCache& info =
163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      g_browser_process->profile_manager()->GetProfileInfoCache();
164a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
165a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string encodedhash =
166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      info.GetLocalAuthCredentialsOfProfileAtIndex(info_index);
167a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (encodedhash.length() == 0 && password.length() == 0)
168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return true;
1691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (!DecodePasswordHashRecord(encodedhash, &record, &encoding))
1701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return false;
1711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string password_hash;
1731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  const char* password_saved;
1741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  const char* password_check;
1751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  size_t password_length;
1761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (encoding == '1') {
178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Validate correct length; extract salt and password hash.
179a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (record.length() != 2 * kHash1Bytes)
1801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      return false;
1811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    std::string salt_str(record.data(), kHash1Bytes);
1821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    password_saved = record.data() + kHash1Bytes;
1831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    password_hash = CreateSecurePasswordHash(salt_str, password, encoding);
1841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    password_check = password_hash.data();
1851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    password_length = kHash1Bytes;
1861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  } else {
1871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // unknown encoding
1881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return false;
1891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
1901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return crypto::SecureMemEqual(password_saved, password_check,
1921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                password_length);
1931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
1941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool ValidateLocalAuthCredentials(const Profile* profile,
196a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  const std::string& password) {
1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return ValidateLocalAuthCredentials(GetProfileInfoIndexOfProfile(profile),
1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                      password);
199a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
200a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
2011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}  // namespace chrome
202