Chromium Code Reviews

Unified Diff: chrome/browser/chromeos/login/parallel_authenticator.cc

Issue 3442009: [Chrome OS] Attempt offline and online login simultaneously (Closed)
Patch Set: Fix crash on data recover Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Index: chrome/browser/chromeos/login/parallel_authenticator.cc
diff --git a/chrome/browser/chromeos/login/parallel_authenticator.cc b/chrome/browser/chromeos/login/parallel_authenticator.cc
new file mode 100644
index 0000000000000000000000000000000000000000..45192173049135644c2bbc1e48ec92835a42898d
--- /dev/null
+++ b/chrome/browser/chromeos/login/parallel_authenticator.cc
@@ -0,0 +1,635 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/parallel_authenticator.h"
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/sha2.h"
+#include "base/string_util.h"
+#include "base/third_party/nss/blapi.h"
+#include "base/third_party/nss/sha256.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/chromeos/cros/cryptohome_library.h"
+#include "chrome/browser/chromeos/login/auth_response_handler.h"
+#include "chrome/browser/chromeos/login/authentication_notification_details.h"
+#include "chrome/browser/chromeos/login/login_status_consumer.h"
+#include "chrome/browser/chromeos/login/ownership_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/net/gaia/gaia_authenticator2.h"
+#include "chrome/common/net/gaia/gaia_constants.h"
+#include "chrome/common/notification_service.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request_status.h"
+#include "third_party/libjingle/source/talk/base/urlencode.h"
+
+using base::Time;
+using base::TimeDelta;
+using file_util::GetFileSize;
+using file_util::PathExists;
+using file_util::ReadFile;
+using file_util::ReadFileToString;
+
+namespace chromeos {
+
+// static
+const char ParallelAuthenticator::kLocalaccountFile[] = "localaccount";
+
+// static
+const int ParallelAuthenticator::kClientLoginTimeoutMs = 10000;
+// static
+const int ParallelAuthenticator::kLocalaccountRetryIntervalMs = 20;
+
+const int kPassHashLen = 32;
+
+ParallelAuthenticator::ParallelAuthenticator(LoginStatusConsumer* consumer)
+ : Authenticator(consumer),
+ already_reported_success_(false),
+ checked_for_localaccount_(false) {
+ CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
+ // If not already owned, this is a no-op. If it is, this loads the owner's
+ // public key off of disk.
+ OwnershipService::GetSharedInstance()->StartLoadOwnerKeyAttempt();
+}
+
+ParallelAuthenticator::~ParallelAuthenticator() {}
+
+bool ParallelAuthenticator::AuthenticateToLogin(
+ Profile* profile,
+ const std::string& username,
+ const std::string& password,
+ const std::string& login_token,
+ const std::string& login_captcha) {
+ current_state_.reset(
+ new AuthAttemptState(Authenticator::Canonicalize(username),
+ password,
+ HashPassword(password),
+ login_token,
+ login_captcha));
+ mounter_ = CryptohomeOp::CreateMountAttempt(current_state_.get(),
+ this,
+ false /* don't create */);
+ current_online_ = new OnlineAttempt(current_state_.get(), this);
+ // Sadly, this MUST be on the UI thread due to sending DBus traffic :-/
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(mounter_.get(), &CryptohomeOp::Initiate));
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(current_online_.get(),
+ &OnlineAttempt::Initiate,
+ profile));
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &ParallelAuthenticator::LoadLocalaccount,
+ std::string(kLocalaccountFile)));
+ return true;
+}
+
+bool ParallelAuthenticator::AuthenticateToUnlock(const std::string& username,
+ const std::string& password) {
+ current_state_.reset(
+ new AuthAttemptState(Authenticator::Canonicalize(username),
+ HashPassword(password)));
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &ParallelAuthenticator::LoadLocalaccount,
+ std::string(kLocalaccountFile)));
+ key_checker_ = CryptohomeOp::CreateCheckKeyAttempt(current_state_.get(),
+ this);
+ // Sadly, this MUST be on the UI thread due to sending DBus traffic :-/
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(key_checker_.get(), &CryptohomeOp::Initiate));
+ return true;
+}
+
+void ParallelAuthenticator::LoginOffTheRecord() {
+ current_state_.reset(new AuthAttemptState("", "", "", "", ""));
+ guest_mounter_ =
+ CryptohomeOp::CreateMountGuestAttempt(current_state_.get(), this);
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ guest_mounter_->Initiate();
+}
+
+void ParallelAuthenticator::OnLoginSuccess(
+ const GaiaAuthConsumer::ClientLoginResult& credentials,
+ bool request_pending) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ LOG(INFO) << "Online login success";
+ // Send notification of success
+ AuthenticationNotificationDetails details(true);
+ NotificationService::current()->Notify(
+ NotificationType::LOGIN_AUTHENTICATION,
+ NotificationService::AllSources(),
+ Details<AuthenticationNotificationDetails>(&details));
+ {
+ AutoLock for_this_block(success_lock_);
+ already_reported_success_ = true;
+ }
+ consumer_->OnLoginSuccess(current_state_->username,
+ credentials,
+ request_pending);
+}
+
+void ParallelAuthenticator::OnOffTheRecordLoginSuccess() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ // Send notification of success
+ AuthenticationNotificationDetails details(true);
+ NotificationService::current()->Notify(
+ NotificationType::LOGIN_AUTHENTICATION,
+ NotificationService::AllSources(),
+ Details<AuthenticationNotificationDetails>(&details));
+ consumer_->OnOffTheRecordLoginSuccess();
+}
+
+void ParallelAuthenticator::OnPasswordChangeDetected(
+ const GaiaAuthConsumer::ClientLoginResult& credentials) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ consumer_->OnPasswordChangeDetected(credentials);
+}
+
+void ParallelAuthenticator::CheckLocalaccount(const LoginFailure& error) {
+ {
+ AutoLock for_this_block(localaccount_lock_);
+ LOG(INFO) << "Checking localaccount";
+ if (!checked_for_localaccount_) {
+ ChromeThread::PostDelayedTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &ParallelAuthenticator::CheckLocalaccount,
+ error),
+ kLocalaccountRetryIntervalMs);
+ return;
+ }
+ }
+
+ if (!localaccount_.empty() && localaccount_ == current_state_->username) {
+ // Success. Go mount a tmpfs for the profile, if necessary.
+ if (!current_state_->unlock) {
+ guest_mounter_ =
+ CryptohomeOp::CreateMountGuestAttempt(current_state_.get(), this);
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(guest_mounter_.get(), &CryptohomeOp::Initiate));
+ } else {
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
+ GaiaAuthConsumer::ClientLoginResult(), false));
+ }
+ } else {
+ // Not the localaccount. Fail, passing along cached error info.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure, error));
+ }
+}
+
+void ParallelAuthenticator::OnLoginFailure(const LoginFailure& error) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ // Send notification of failure
+ AuthenticationNotificationDetails details(false);
+ NotificationService::current()->Notify(
+ NotificationType::LOGIN_AUTHENTICATION,
+ NotificationService::AllSources(),
+ Details<AuthenticationNotificationDetails>(&details));
+ LOG(WARNING) << "Login failed: " << error.GetErrorString();
+ consumer_->OnLoginFailure(error);
+}
+
+void ParallelAuthenticator::RecoverEncryptedData(
+ const std::string& old_password,
+ const GaiaAuthConsumer::ClientLoginResult& credentials) {
+ std::string old_hash = HashPassword(old_password);
+ key_migrator_ = CryptohomeOp::CreateMigrateAttempt(current_state_.get(),
+ this,
+ true,
+ old_hash);
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &ParallelAuthenticator::ResyncRecoverHelper,
+ key_migrator_.get()));
+}
+
+void ParallelAuthenticator::ResyncEncryptedData(
+ const GaiaAuthConsumer::ClientLoginResult& credentials) {
+ data_remover_ =
+ CryptohomeOp::CreateRemoveAttempt(current_state_.get(), this);
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &ParallelAuthenticator::ResyncRecoverHelper,
+ data_remover_.get()));
+}
+
+void ParallelAuthenticator::ResyncRecoverHelper(CryptohomeOp* to_initiate) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ current_state_->ResetCryptohomeStatus();
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(to_initiate, &CryptohomeOp::Initiate));
+}
+
+void ParallelAuthenticator::RetryAuth(Profile* profile,
+ const std::string& username,
+ const std::string& password,
+ const std::string& login_token,
+ const std::string& login_captcha) {
+ reauth_state_.reset(
+ new AuthAttemptState(Authenticator::Canonicalize(username),
+ password,
+ HashPassword(password),
+ login_token,
+ login_captcha));
+ current_online_ = new OnlineAttempt(reauth_state_.get(), this);
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(current_online_.get(),
+ &OnlineAttempt::Initiate,
+ profile));
+}
+
+void ParallelAuthenticator::Resolve() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ bool request_pending = false;
+ bool create = false;
+ switch (ResolveState()) {
+ case CONTINUE:
+ case POSSIBLE_PW_CHANGE:
+ case NO_MOUNT:
+ // These are intermediate states; we need more info from a request that
+ // is still pending.
+ break;
+ case FAILED_MOUNT:
+ // In this case, whether login succeeded or not, we can't log
+ // the user in because their data is horked. So, override with
+ // the appropriate failure.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &ParallelAuthenticator::OnLoginFailure,
+ LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME)));
+ break;
+ case FAILED_REMOVE:
+ // In this case, we tried to remove the user's old cryptohome at her
+ // request, and the remove failed.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
+ LoginFailure(LoginFailure::DATA_REMOVAL_FAILED)));
+ break;
+ case FAILED_TMPFS:
+ // In this case, we tried to mount a tmpfs for BWSI or the localaccount
+ // user and failed.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
+ LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)));
+ break;
+ case CREATE_NEW:
+ create = true;
+ case RECOVER_MOUNT:
+ current_state_->ResetCryptohomeStatus();
+ mounter_ = CryptohomeOp::CreateMountAttempt(current_state_.get(),
+ this,
+ create);
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(mounter_.get(), &CryptohomeOp::Initiate));
+ break;
+ case NEED_OLD_PW:
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &ParallelAuthenticator::OnPasswordChangeDetected,
+ current_state_->credentials()));
+ break;
+ case ONLINE_FAILED:
+ // In this case, we know online login was rejected because the account
+ // is disabled or something similarly fatal. Sending the user through
+ // the same path they get when their password is rejected is cleaner
+ // for now.
+ // TODO(cmasone): optimize this so that we don't send the user through
+ // the 'changed password' path when we know doing so won't succeed.
+ case NEED_NEW_PW:
+ {
+ AutoLock for_this_block(success_lock_);
+ if (!already_reported_success_) {
+ // This allows us to present the same behavior for "online:
+ // fail, offline: ok", regardless of the order in which we
+ // receive the results. There will be cases in which we get
+ // the online failure some time after the offline success,
+ // so we just force all cases in this category to present like this:
+ // OnLoginSuccess(..., ..., true) -> OnLoginFailure().
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
+ current_state_->credentials(), true));
+ }
+ }
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
+ current_state_->online_outcome()));
+ break;
+ case HAVE_NEW_PW:
+ key_migrator_ =
+ CryptohomeOp::CreateMigrateAttempt(reauth_state_.get(),
+ this,
+ false,
+ reauth_state_->ascii_hash);
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(key_migrator_.get(), &CryptohomeOp::Initiate));
+ break;
+ case OFFLINE_LOGIN:
+ request_pending = !current_state_->online_complete();
+ // Fall through.
+ case UNLOCK:
+ // Fall through.
+ case ONLINE_LOGIN:
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
+ current_state_->credentials(), request_pending));
+ break;
+ case LOCAL_LOGIN:
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &ParallelAuthenticator::OnOffTheRecordLoginSuccess));
+ break;
+ case LOGIN_FAILED:
+ current_state_->ResetCryptohomeStatus();
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &ParallelAuthenticator::CheckLocalaccount,
+ current_state_->online_outcome()));
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+ParallelAuthenticator::AuthState ParallelAuthenticator::ResolveState() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ // If we haven't mounted the user's home dir yet, we can't be done.
+ // We never get past here if a cryptohome op is still pending.
+ // This is an important invariant.
+ if (!current_state_->cryptohome_complete())
+ return CONTINUE;
+
+ AuthState state = (reauth_state_.get() ? ResolveReauthState() : CONTINUE);
+ if (state != CONTINUE)
+ return state;
+
+ if (current_state_->cryptohome_outcome())
+ state = ResolveCryptohomeSuccessState();
+ else
+ state = ResolveCryptohomeFailureState();
+
+ DCHECK(current_state_->cryptohome_complete()); // Ensure invariant holds.
+ key_migrator_ = NULL;
+ data_remover_ = NULL;
+ guest_mounter_ = NULL;
+ key_checker_ = NULL;
+
+ if (state != POSSIBLE_PW_CHANGE &&
+ state != NO_MOUNT &&
+ state != OFFLINE_LOGIN)
+ return state;
+
+ if (current_state_->online_complete()) {
+ if (current_state_->online_outcome().reason() == LoginFailure::NONE) {
+ // Online attempt succeeded as well, so combine the results.
+ return ResolveOnlineSuccessState(state);
+ } else {
+ // Online login attempt was rejected or failed to occur.
+ return ResolveOnlineFailureState(state);
+ }
+ }
+ // if online isn't complete yet, just return the offline result.
+ return state;
+}
+
+ParallelAuthenticator::AuthState
+ParallelAuthenticator::ResolveReauthState() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ if (reauth_state_->cryptohome_complete()) {
+ if (!reauth_state_->cryptohome_outcome()) {
+ // If we've tried to migrate and failed, log the error and just wait
+ // til next time the user logs in to migrate their cryptohome key.
+ LOG(ERROR) << "Failed to migrate cryptohome key: "
+ << reauth_state_->cryptohome_code();
+ }
+ reauth_state_.reset(NULL);
+ return ONLINE_LOGIN;
+ }
+ // Haven't tried the migrate yet, must be processing the online auth attempt.
+ if (!reauth_state_->online_complete()) {
+ NOTREACHED(); // Shouldn't be here at all, if online reauth isn't done!
+ return CONTINUE;
+ }
+ if (reauth_state_->online_outcome().reason() == LoginFailure::NONE)
+ return HAVE_NEW_PW;
+ else
+ return NEED_NEW_PW;
+}
+
+ParallelAuthenticator::AuthState
+ParallelAuthenticator::ResolveCryptohomeFailureState() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ if (data_remover_.get()) {
+ return FAILED_REMOVE;
+ } else if (guest_mounter_.get()) {
+ return FAILED_TMPFS;
+ } else if (key_migrator_.get()) {
+ return NEED_OLD_PW;
+ } else if (key_checker_.get()) {
+ return LOGIN_FAILED;
+ } else if (current_state_->cryptohome_code() ==
+ chromeos::kCryptohomeMountErrorKeyFailure) {
+ // If we tried a mount but they used the wrong key, we may need to
+ // ask the user for her old password. We'll only know once we've
+ // done the online check.
+ return POSSIBLE_PW_CHANGE;
+ } else if (current_state_->cryptohome_code() ==
+ chromeos::kCryptohomeMountErrorUserDoesNotExist) {
+ // If we tried a mount but the user did not exist, then we should wait
+ // for online login to succeed and try again with the "create" flag set.
+ return NO_MOUNT;
+ } else {
+ return FAILED_MOUNT;
+ }
+}
+
+ParallelAuthenticator::AuthState
+ParallelAuthenticator::ResolveCryptohomeSuccessState() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ if (data_remover_.get()) {
+ return CREATE_NEW;
+ } else if (guest_mounter_.get()) {
+ return LOCAL_LOGIN;
+ } else if (key_migrator_.get()) {
+ return RECOVER_MOUNT;
+ } else if (key_checker_.get()) {
+ return UNLOCK;
+ } else {
+ return OFFLINE_LOGIN;
+ }
+}
+
+ParallelAuthenticator::AuthState
+ParallelAuthenticator::ResolveOnlineFailureState(
+ ParallelAuthenticator::AuthState offline_state) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ if (offline_state == OFFLINE_LOGIN) {
+ if (current_state_->online_outcome().error().state() ==
+ GoogleServiceAuthError::CONNECTION_FAILED) {
+ // Couldn't do an online check, so just go with the offline result.
+ return OFFLINE_LOGIN;
+ }
+ // Otherwise, online login was rejected!
+ if (current_state_->online_outcome().error().state() ==
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
+ return NEED_NEW_PW;
+ } else {
+ return ONLINE_FAILED;
+ }
+ }
+ return LOGIN_FAILED;
+}
+
+ParallelAuthenticator::AuthState
+ParallelAuthenticator::ResolveOnlineSuccessState(
+ ParallelAuthenticator::AuthState offline_state) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ switch (offline_state) {
+ case POSSIBLE_PW_CHANGE:
+ return NEED_OLD_PW;
+ case NO_MOUNT:
+ return CREATE_NEW;
+ case OFFLINE_LOGIN:
+ return ONLINE_LOGIN;
+ default:
+ NOTREACHED();
+ return offline_state;
+ }
+}
+
+void ParallelAuthenticator::LoadSystemSalt() {
+ if (!system_salt_.empty())
+ return;
+ system_salt_ = CrosLibrary::Get()->GetCryptohomeLibrary()->GetSystemSalt();
+ CHECK(!system_salt_.empty());
+ CHECK_EQ(system_salt_.size() % 2, 0U);
+}
+
+void ParallelAuthenticator::LoadLocalaccount(const std::string& filename) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
+ {
+ AutoLock for_this_block(localaccount_lock_);
+ if (checked_for_localaccount_)
+ return;
+ }
+ FilePath localaccount_file;
+ std::string localaccount;
+ if (PathService::Get(base::DIR_EXE, &localaccount_file)) {
+ localaccount_file = localaccount_file.Append(filename);
+ LOG(INFO) << "looking for localaccount in " << localaccount_file.value();
+
+ ReadFileToString(localaccount_file, &localaccount);
+ TrimWhitespaceASCII(localaccount, TRIM_TRAILING, &localaccount);
+ LOG(INFO) << "Loading localaccount: " << localaccount;
+ } else {
+ LOG(INFO) << "Assuming no localaccount";
+ }
+ SetLocalaccount(localaccount);
+}
+
+void ParallelAuthenticator::SetLocalaccount(const std::string& new_name) {
+ localaccount_ = new_name;
+ { // extra braces for clarity about AutoLock scope.
+ AutoLock for_this_block(localaccount_lock_);
+ checked_for_localaccount_ = true;
+ }
+}
+
+
+std::string ParallelAuthenticator::HashPassword(const std::string& password) {
+ // Get salt, ascii encode, update sha with that, then update with ascii
+ // of password, then end.
+ std::string ascii_salt = SaltAsAscii();
+ unsigned char passhash_buf[kPassHashLen];
+ char ascii_buf[kPassHashLen + 1];
+
+ // Hash salt and password
+ SHA256Context ctx;
+ SHA256_Begin(&ctx);
+ SHA256_Update(&ctx,
+ reinterpret_cast<const unsigned char*>(ascii_salt.data()),
+ static_cast<unsigned int>(ascii_salt.length()));
+ SHA256_Update(&ctx,
+ reinterpret_cast<const unsigned char*>(password.data()),
+ static_cast<unsigned int>(password.length()));
+ SHA256_End(&ctx,
+ passhash_buf,
+ NULL,
+ static_cast<unsigned int>(sizeof(passhash_buf)));
+
+ std::vector<unsigned char> passhash(passhash_buf,
+ passhash_buf + sizeof(passhash_buf));
+ BinaryToHex(passhash,
+ passhash.size() / 2, // only want top half, at least for now.
+ ascii_buf,
+ sizeof(ascii_buf));
+ return std::string(ascii_buf, sizeof(ascii_buf) - 1);
+}
+
+std::string ParallelAuthenticator::SaltAsAscii() {
+ LoadSystemSalt(); // no-op if it's already loaded.
+ unsigned int salt_len = system_salt_.size();
+ char ascii_salt[2 * salt_len + 1];
+ if (ParallelAuthenticator::BinaryToHex(system_salt_,
+ salt_len,
+ ascii_salt,
+ sizeof(ascii_salt))) {
+ return std::string(ascii_salt, sizeof(ascii_salt) - 1);
+ } else {
+ return std::string();
+ }
+}
+
+// static
+bool ParallelAuthenticator::BinaryToHex(
+ const std::vector<unsigned char>& binary,
+ const unsigned int binary_len,
+ char* hex_string,
+ const unsigned int len) {
+ if (len < 2*binary_len)
+ return false;
+ memset(hex_string, 0, len);
+ for (uint i = 0, j = 0; i < binary_len; i++, j+=2)
+ snprintf(hex_string + j, len - j, "%02x", binary[i]);
+ return true;
+}
+
+} // namespace chromeos

Powered by Google App Engine