summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorzea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-03 00:57:52 +0000
committerzea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-03 00:57:52 +0000
commita76abe7de93690d517f4279378dd496305a79e64 (patch)
tree74bb1b1b1c257ededbccafffb6263b247cfc82f0 /chrome
parent94714db540723a0e76c264a98ac61dde61e04c2e (diff)
downloadchromium_src-a76abe7de93690d517f4279378dd496305a79e64.zip
chromium_src-a76abe7de93690d517f4279378dd496305a79e64.tar.gz
chromium_src-a76abe7de93690d517f4279378dd496305a79e64.tar.bz2
[Sync] Ensure we always re-encrypt with newest gaia password if available.
Previously we would only re-encrypt an account that had implicit passphrases if, on a password change, the client was already signed in. Otherwise, for example if the user signs in to a new client, we would disregard the current credentials because they are unable to decrypt the existing encrypted data (based on the old gaia password). This change ensures we do not throw away the old credentials. If we have pending keys, we go ahead and generate new encryption keys based on the new credentials and set those keys as the default. In addition, we persist the keys in the bootstrap token for the cryptographer. In this way, the bootstrap token for accounts using implicit passphrases will always correspond to the encryption keys derived from the most recent gaia password. Once the user supplies the old gaia password (which they will be prompted for due to the cryptographer having pending keys), we will install it, but then restore the encryption keys based on the current gaia password as the default. This decision is made based on whether the pending keybag contains the current default key or not. If it does, then the keybag must be newer than our data, and we let it overwrite the default. If it does not, then our current key is the newest, and we persist the current default. In order to support this change, we plumb from the setup flow whether a passphrase originates from the user or from an internal source (the signin successful notification). In this way we can differentiate the current implicit passphrase from an older user provided one. BUG=104508 TEST=sync_unit_tests. All existing passphrase tests. Manually signing into a clean account, setting up sync, signing out, changing the password, then signing in to a new client. The new client should prompt for the old password, but once provided should re-encrypt all data with the new password. Subsequent new clients should not prompt for any old password. Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=120157 Review URL: https://chromiumcodereview.appspot.com/9309022 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@120257 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/chromeos/login/login_utils.cc7
-rw-r--r--chrome/browser/chromeos/login/screen_locker.cc4
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.cc35
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.h19
-rw-r--r--chrome/browser/sync/internal_api/debug_info_event_listener.cc8
-rw-r--r--chrome/browser/sync/internal_api/debug_info_event_listener.h3
-rw-r--r--chrome/browser/sync/internal_api/sync_manager.cc322
-rw-r--r--chrome/browser/sync/internal_api/sync_manager.h35
-rw-r--r--chrome/browser/sync/internal_api/syncapi_unittest.cc260
-rw-r--r--chrome/browser/sync/js/js_sync_manager_observer.cc14
-rw-r--r--chrome/browser/sync/js/js_sync_manager_observer.h3
-rw-r--r--chrome/browser/sync/js/js_sync_manager_observer_unittest.cc4
-rw-r--r--chrome/browser/sync/profile_sync_service.cc37
-rw-r--r--chrome/browser/sync/profile_sync_service.h30
-rw-r--r--chrome/browser/sync/profile_sync_service_harness.cc4
-rw-r--r--chrome/browser/sync/profile_sync_service_password_unittest.cc4
-rw-r--r--chrome/browser/sync/protocol/client_debug_info.proto2
-rw-r--r--chrome/browser/sync/sync_setup_flow.cc12
-rw-r--r--chrome/browser/sync/sync_setup_wizard_unittest.cc3
-rw-r--r--chrome/browser/sync/test/integration/passwords_helper.cc6
-rw-r--r--chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc10
-rw-r--r--chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc16
-rw-r--r--chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc50
-rw-r--r--chrome/browser/sync/util/cryptographer.cc11
-rw-r--r--chrome/browser/sync/util/cryptographer.h5
25 files changed, 747 insertions, 157 deletions
diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc
index 2c4c5ab..158ac63 100644
--- a/chrome/browser/chromeos/login/login_utils.cc
+++ b/chrome/browser/chromeos/login/login_utils.cc
@@ -921,8 +921,11 @@ void LoginUtilsImpl::StartSignedInServices(
// data encrypted with the user's GAIA password.
ProfileSyncService* sync_service =
ProfileSyncServiceFactory::GetInstance()->GetForProfile(user_profile);
- if (sync_service)
- sync_service->SetPassphrase(password_, false);
+ if (sync_service) {
+ sync_service->SetPassphrase(password_,
+ ProfileSyncService::IMPLICIT,
+ ProfileSyncService::INTERNAL);
+ }
}
password_.clear();
TokenService* token_service = user_profile->GetTokenService();
diff --git a/chrome/browser/chromeos/login/screen_locker.cc b/chrome/browser/chromeos/login/screen_locker.cc
index 75aa7db4..a751ba5 100644
--- a/chrome/browser/chromeos/login/screen_locker.cc
+++ b/chrome/browser/chromeos/login/screen_locker.cc
@@ -271,7 +271,9 @@ void ScreenLocker::OnLoginSuccess(
ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile));
if (service && !service->HasSyncSetupCompleted()) {
// If sync has failed somehow, try setting the sync passphrase here.
- service->SetPassphrase(password, false);
+ service->SetPassphrase(password,
+ ProfileSyncService::IMPLICIT,
+ ProfileSyncService::INTERNAL);
}
}
DBusThreadManager::Get()->GetPowerManagerClient()->
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index 77bb4bc..9803dda 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -82,7 +82,8 @@ class SyncBackendHost::Core
virtual void OnPassphraseRequired(
sync_api::PassphraseRequiredReason reason,
const sync_pb::EncryptedData& pending_keys) OVERRIDE;
- virtual void OnPassphraseAccepted(
+ virtual void OnPassphraseAccepted() OVERRIDE;
+ virtual void OnBootstrapTokenUpdated(
const std::string& bootstrap_token) OVERRIDE;
virtual void OnStopSyncingPermanently() OVERRIDE;
virtual void OnUpdatedToken(const std::string& token) OVERRIDE;
@@ -125,7 +126,9 @@ class SyncBackendHost::Core
// Called to set the passphrase on behalf of
// SyncBackendHost::SupplyPassphrase.
- void DoSetPassphrase(const std::string& passphrase, bool is_explicit);
+ void DoSetPassphrase(const std::string& passphrase,
+ bool is_explicit,
+ bool user_provided);
// Called to turn on encryption of all sync data as well as
// reencrypt everything.
@@ -323,7 +326,8 @@ void SyncBackendHost::StartSyncingWithServer() {
}
void SyncBackendHost::SetPassphrase(const std::string& passphrase,
- bool is_explicit) {
+ bool is_explicit,
+ bool user_provided) {
if (!IsNigoriEnabled()) {
SLOG(WARNING) << "Silently dropping SetPassphrase request.";
return;
@@ -335,7 +339,7 @@ void SyncBackendHost::SetPassphrase(const std::string& passphrase,
// If encryption is enabled and we've got a SetPassphrase
sync_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&SyncBackendHost::Core::DoSetPassphrase, core_.get(),
- passphrase, is_explicit));
+ passphrase, is_explicit, user_provided));
}
void SyncBackendHost::StopSyncManagerForShutdown(
@@ -804,14 +808,23 @@ void SyncBackendHost::Core::OnPassphraseRequired(
&SyncBackendHost::NotifyPassphraseRequired, reason, pending_keys);
}
-void SyncBackendHost::Core::OnPassphraseAccepted(
+void SyncBackendHost::Core::OnPassphraseAccepted() {
+ if (!sync_loop_)
+ return;
+ DCHECK_EQ(MessageLoop::current(), sync_loop_);
+ host_.Call(
+ FROM_HERE,
+ &SyncBackendHost::NotifyPassphraseAccepted);
+}
+
+void SyncBackendHost::Core::OnBootstrapTokenUpdated(
const std::string& bootstrap_token) {
if (!sync_loop_)
return;
DCHECK_EQ(MessageLoop::current(), sync_loop_);
host_.Call(
FROM_HERE,
- &SyncBackendHost::NotifyPassphraseAccepted, bootstrap_token);
+ &SyncBackendHost::PersistEncryptionBootstrapToken, bootstrap_token);
}
void SyncBackendHost::Core::OnStopSyncingPermanently() {
@@ -981,9 +994,10 @@ void SyncBackendHost::Core::DoRequestCleanupDisabledTypes() {
}
void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase,
- bool is_explicit) {
+ bool is_explicit,
+ bool user_provided) {
DCHECK_EQ(MessageLoop::current(), sync_loop_);
- sync_manager_->SetPassphrase(passphrase, is_explicit);
+ sync_manager_->SetPassphrase(passphrase, is_explicit, user_provided);
}
void SyncBackendHost::Core::DoEnableEncryptEverything() {
@@ -1184,8 +1198,7 @@ void SyncBackendHost::NotifyPassphraseRequired(
frontend_->OnPassphraseRequired(reason, pending_keys);
}
-void SyncBackendHost::NotifyPassphraseAccepted(
- const std::string& bootstrap_token) {
+void SyncBackendHost::NotifyPassphraseAccepted() {
if (!frontend_)
return;
@@ -1193,8 +1206,6 @@ void SyncBackendHost::NotifyPassphraseAccepted(
// Clear our cache of the cryptographer's pending keys.
cached_pending_keys_.clear_blob();
-
- PersistEncryptionBootstrapToken(bootstrap_token);
frontend_->OnPassphraseAccepted();
}
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index bbb8bb7..6f508e5 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -179,11 +179,18 @@ class SyncBackendHost : public BackendDataTypeConfigurer {
virtual void StartSyncingWithServer();
// Called on |frontend_loop_| to asynchronously set the passphrase.
- // |is_explicit| is true if the call is in response to the user explicitly
- // setting a passphrase as opposed to implicitly (from the users' perspective)
- // using their Google Account password. An implicit SetPassphrase will *not*
- // *not* override an explicit passphrase set previously.
- void SetPassphrase(const std::string& passphrase, bool is_explicit);
+ // |is_explicit| is true if the call is in response to the user setting a
+ // custom explicit passphrase as opposed to implicitly (from the users'
+ // perspective) using their Google Account password. An implicit SetPassphrase
+ // will *not* override an explicit passphrase set previously. Note that
+ // if the data is encrypted with an old Google Account password, the user
+ // may still have to provide a "implicit" passphrase.
+ // |user_provided| corresponds to the user having manually provided this
+ // passphrase. It should only be false for passphrases intercepted from the
+ // Google Sign-in Success notification and true otherwise.
+ void SetPassphrase(const std::string& passphrase,
+ bool is_explicit,
+ bool user_provided);
// Called on |frontend_loop_| to kick off shutdown procedure. After this,
// no further sync activity will occur with the sync server and no further
@@ -407,7 +414,7 @@ class SyncBackendHost : public BackendDataTypeConfigurer {
sync_pb::EncryptedData pending_keys);
// Invoked when the passphrase provided by the user has been accepted.
- void NotifyPassphraseAccepted(const std::string& bootstrap_token);
+ void NotifyPassphraseAccepted();
// Invoked when an updated token is available from the sync server.
void NotifyUpdatedToken(const std::string& token);
diff --git a/chrome/browser/sync/internal_api/debug_info_event_listener.cc b/chrome/browser/sync/internal_api/debug_info_event_listener.cc
index 239e42b..d783dee 100644
--- a/chrome/browser/sync/internal_api/debug_info_event_listener.cc
+++ b/chrome/browser/sync/internal_api/debug_info_event_listener.cc
@@ -48,11 +48,15 @@ void DebugInfoEventListener::OnPassphraseRequired(
CreateAndAddEvent(sync_pb::DebugEventInfo::PASSPHRASE_REQUIRED);
}
-void DebugInfoEventListener::OnPassphraseAccepted(
- const std::string& bootstrap_token) {
+void DebugInfoEventListener::OnPassphraseAccepted() {
CreateAndAddEvent(sync_pb::DebugEventInfo::PASSPHRASE_ACCEPTED);
}
+void DebugInfoEventListener::OnBootstrapTokenUpdated(
+ const std::string& bootstrap_token) {
+ CreateAndAddEvent(sync_pb::DebugEventInfo::BOOTSTRAP_TOKEN_UPDATED);
+}
+
void DebugInfoEventListener::OnStopSyncingPermanently() {
CreateAndAddEvent(sync_pb::DebugEventInfo::STOP_SYNCING_PERMANENTLY);
}
diff --git a/chrome/browser/sync/internal_api/debug_info_event_listener.h b/chrome/browser/sync/internal_api/debug_info_event_listener.h
index 98ecfdf..a4daa18 100644
--- a/chrome/browser/sync/internal_api/debug_info_event_listener.h
+++ b/chrome/browser/sync/internal_api/debug_info_event_listener.h
@@ -40,7 +40,8 @@ class DebugInfoEventListener : public sync_api::SyncManager::Observer,
virtual void OnPassphraseRequired(
sync_api::PassphraseRequiredReason reason,
const sync_pb::EncryptedData& pending_keys) OVERRIDE;
- virtual void OnPassphraseAccepted(
+ virtual void OnPassphraseAccepted() OVERRIDE;
+ virtual void OnBootstrapTokenUpdated(
const std::string& bootstrap_token) OVERRIDE;
virtual void OnStopSyncingPermanently() OVERRIDE;
virtual void OnUpdatedToken(const std::string& token) OVERRIDE;
diff --git a/chrome/browser/sync/internal_api/sync_manager.cc b/chrome/browser/sync/internal_api/sync_manager.cc
index 09739d2..a93e256 100644
--- a/chrome/browser/sync/internal_api/sync_manager.cc
+++ b/chrome/browser/sync/internal_api/sync_manager.cc
@@ -241,7 +241,19 @@ class SyncManager::SyncInternal
// Try to set the current passphrase to |passphrase|, and record whether
// it is an explicit passphrase or implicitly using gaia in the Nigori
// node.
- void SetPassphrase(const std::string& passphrase, bool is_explicit);
+ // |is_explicit| is true if the call is in response to the user setting a
+ // custom explicit passphrase as opposed to implicitly (from the users'
+ // perspective) using their Google Account password. Once an explicit
+ // passphrase is set, it can never be overwritten (not even by another
+ // explicit passphrase).
+ // |user_provided| is true corresponds to the user having manually provided
+ // this passphrase. It should only be false for passphrases intercepted
+ // from the Google Sign-in Success notification. Note that if the data is
+ // encrypted with an old Google Account password, the user may still have to
+ // provide an "implicit" passphrase.
+ void SetPassphrase(const std::string& passphrase,
+ bool is_explicit,
+ bool user_provided);
// Call periodically from a database-safe thread to persist recent changes
// to the syncapi model.
@@ -386,6 +398,25 @@ class SyncManager::SyncInternal
// available.
void RaiseAuthNeededEvent();
+ // Helpers for SetPassphrase. TODO(rsimha): make these the public methods
+ // eventually and have them replace SetPassphrase(..).
+ // These correspond to setting a passphrase for decryption (when we have
+ // pending keys) or setting a passphrase for encryption (we do not have
+ // pending keys).
+ bool SetDecryptionPassphrase(
+ const KeyParams& key_params,
+ bool nigori_has_explicit_passphrase,
+ bool is_explicit,
+ bool user_provided,
+ Cryptographer* cryptographer,
+ std::string *new_bootstrap_token);
+ bool SetEncryptionPassphrase(
+ const KeyParams& key_params,
+ bool nigori_has_explicit_passphrase,
+ bool is_explicit,
+ Cryptographer* cryptographer,
+ std::string *new_bootstrap_token);
+
// Internal callback of UpdateCryptographerAndNigoriCallback.
void UpdateCryptographerAndNigoriCallback(
const base::Callback<void(bool)>& done_callback,
@@ -742,9 +773,10 @@ void SyncManager::StartSyncingNormally() {
}
void SyncManager::SetPassphrase(const std::string& passphrase,
- bool is_explicit) {
+ bool is_explicit,
+ bool user_provided) {
DCHECK(thread_checker_.CalledOnValidThread());
- data_->SetPassphrase(passphrase, is_explicit);
+ data_->SetPassphrase(passphrase, is_explicit, user_provided);
}
void SyncManager::EnableEncryptEverything() {
@@ -1168,7 +1200,8 @@ void SyncManager::SyncInternal::RaiseAuthNeededEvent() {
}
void SyncManager::SyncInternal::SetPassphrase(
- const std::string& passphrase, bool is_explicit) {
+ const std::string& passphrase, bool is_explicit, bool user_provided) {
+ DCHECK(user_provided || !is_explicit);
// We do not accept empty passphrases.
if (passphrase.empty()) {
DVLOG(1) << "Rejecting empty passphrase.";
@@ -1186,7 +1219,7 @@ void SyncManager::SyncInternal::SetPassphrase(
// All accesses to the cryptographer are protected by a transaction.
WriteTransaction trans(FROM_HERE, GetUserShare());
Cryptographer* cryptographer = trans.GetCryptographer();
- KeyParams params = {"localhost", "dummy", passphrase};
+ KeyParams key_params = {"localhost", "dummy", passphrase};
WriteNode node(&trans);
if (!node.InitByTagLookup(kNigoriTag)) {
@@ -1194,49 +1227,83 @@ void SyncManager::SyncInternal::SetPassphrase(
NOTREACHED();
return;
}
-
+ bool nigori_has_explicit_passphrase =
+ node.GetNigoriSpecifics().using_explicit_passphrase();
+
+ // There are five cases to handle here:
+ // 1. The user has no pending keys and is setting their current GAIA password
+ // as the encryption passphrase. This happens either during first time sync
+ // with a clean profile, or after re-authenticating on a profile that was
+ // already signed in with the cryptographer ready.
+ // 2. The user is overwriting an (already provided) implicit passphrase with
+ // an explicit (custom) passphrase. There are no pending keys.
+ // 3. We're using the current GAIA password to decrypt the pending keys. This
+ // happens when signing in to an account with a previously set implicit
+ // passphrase, where the data is already encrypted with the newest GAIA
+ // password.
+ // 4. The user is providing an old GAIA password to decrypt the pending keys.
+ // In this case, the user is using an implicit passphrase, but has changed
+ // their password since they last encrypted their data, and therefore
+ // their current GAIA password was unable to decrypt the data. This will
+ // happen when the user is setting up a new profile with a previously
+ // encrypted account (after changing passwords).
+ // 5. The user is providing a previously set explicit passphrase to decrypt
+ // the pending keys.
+ // Furthermore, we enforce the following: The bootstrap encryption token will
+ // always be derived from the newest GAIA password if the account is using
+ // an implicit passphrase (even if the data is encrypted with an old GAIA
+ // password). If the account is using an explicit (custom) passphrase, the
+ // bootstrap token will be derived from the most recently provided explicit
+ // passphrase (that was able to decrypt the data).
+ // TODO(rsimha): Fix the plumbing so we call these two methods separately and
+ // directly from the PSS API. It may also make sense to embed this logic
+ // within the cryptographer itself. http://crbug.com/108718
+ std::string bootstrap_token;
+ bool success = false;
+ sync_pb::EncryptedData pending_keys;
if (cryptographer->has_pending_keys()) {
- bool succeeded = false;
-
- // See if the explicit flag matches what is set in nigori. If not we dont
- // even try the passphrase. Note: This could mean that we wont try setting
- // the gaia password as passphrase if custom is elected by the user. Which
- // is fine because nigori node has all the old passwords in it.
- if (node.GetNigoriSpecifics().using_explicit_passphrase() == is_explicit) {
- if (cryptographer->DecryptPendingKeys(params)) {
- succeeded = true;
- } else {
- DVLOG(1) << "Passphrase failed to decrypt pending keys.";
- }
- } else {
- DVLOG(1) << "Not trying the passphrase because the explicit flags dont "
- << "match. Nigori node's explicit flag is "
- << node.GetNigoriSpecifics().using_explicit_passphrase();
- }
-
- if (!succeeded) {
- sync_pb::EncryptedData pending_keys;
- if (cryptographer->has_pending_keys())
- pending_keys = cryptographer->GetPendingKeys();
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED,
- pending_keys));
- return;
+ pending_keys = cryptographer->GetPendingKeys();
+ // Handles cases 3, 4, and 5.
+ success = SetDecryptionPassphrase(key_params,
+ nigori_has_explicit_passphrase,
+ is_explicit,
+ user_provided,
+ cryptographer,
+ &bootstrap_token);
+ if (success) {
+ // Nudge the syncer so that encrypted datatype updates that were waiting
+ // for this passphrase get applied as soon as possible.
+ RequestNudge(FROM_HERE);
}
-
- // Nudge the syncer so that encrypted datatype updates that were waiting for
- // this passphrase get applied as soon as possible.
- RequestNudge(FROM_HERE);
} else {
- DVLOG(1) << "No pending keys, adding provided passphrase.";
-
- // Prevent an implicit SetPassphrase request from changing an explicitly
- // set passphrase.
- if (!is_explicit && node.GetNigoriSpecifics().using_explicit_passphrase())
- return;
+ // Handles cases 1 and 2.
+ success = SetEncryptionPassphrase(key_params,
+ nigori_has_explicit_passphrase,
+ is_explicit,
+ cryptographer,
+ &bootstrap_token);
+ }
+
+ // It's possible we need to change the bootstrap token even if we failed to
+ // set the passphrase (for example if we need to preserve the new GAIA
+ // passphrase).
+ if (!bootstrap_token.empty()) {
+ DVLOG(1) << "Bootstrap token updated.";
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnBootstrapTokenUpdated(bootstrap_token));
+ }
- cryptographer->AddKey(params);
+ if (!success) {
+ DVLOG(1) << "SetPassphrase failure, notifying and returning.";
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED,
+ pending_keys));
+ return;
}
+ DVLOG(1) << "SetPassphrase success, updating nigori and reencrypting.";
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnPassphraseAccepted());
+ DCHECK(cryptographer->is_ready());
// TODO(tim): Bug 58231. It would be nice if SetPassphrase didn't require
// messing with the Nigori node, because we can't call SetPassphrase until
@@ -1255,12 +1322,168 @@ void SyncManager::SyncInternal::SetPassphrase(
// Does nothing if everything is already encrypted or the cryptographer has
// pending keys.
ReEncryptEverything(&trans);
+}
- DVLOG(1) << "Passphrase accepted, bootstrapping encryption.";
- std::string bootstrap_token;
- cryptographer->GetBootstrapToken(&bootstrap_token);
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPassphraseAccepted(bootstrap_token));
+bool SyncManager::SyncInternal::SetEncryptionPassphrase(
+ const KeyParams& key_params,
+ bool nigori_has_explicit_passphrase,
+ bool is_explicit,
+ Cryptographer* cryptographer,
+ std::string *bootstrap_token) {
+ if (cryptographer->has_pending_keys()) {
+ LOG(ERROR) << "Attempt to set encryption passphrase failed because there "
+ << "were pending keys.";
+ return false;
+ }
+ if (!nigori_has_explicit_passphrase) {
+ // Case 1 and 2. Setting a new GAIA passphrase when there are no pending
+ // keys (1), or overwriting an implicit passphrase with a new explicit one
+ // (2) when there are no pending keys.
+ if (cryptographer->AddKey(key_params)) {
+ DVLOG(1) << "Setting " << (is_explicit ? "explicit" : "implicit" )
+ << " passphrase for encryption.";
+ cryptographer->GetBootstrapToken(bootstrap_token);
+ return true;
+ } else {
+ NOTREACHED() << "Failed to add key to cryptographer.";
+ return false;
+ }
+ } else { // nigori_has_explicit_passphrase == true
+ NOTREACHED() << "Attempting to change explicit passphrase when one has "
+ << "already been set.";
+ return false;
+ } // nigori_has_explicit_passphrase
+ NOTREACHED();
+ return false;
+}
+
+bool SyncManager::SyncInternal::SetDecryptionPassphrase(
+ const KeyParams& key_params,
+ bool nigori_has_explicit_passphrase,
+ bool is_explicit,
+ bool user_provided,
+ Cryptographer* cryptographer,
+ std::string *bootstrap_token) {
+ if (!cryptographer->has_pending_keys()) {
+ NOTREACHED() << "Attempt to set decryption passphrase failed because there "
+ << "were no pending keys.";
+ return false;
+
+ }
+ if (!nigori_has_explicit_passphrase) {
+ if (!is_explicit) {
+ if (!user_provided) {
+ // Case 3.
+ if (cryptographer->DecryptPendingKeys(key_params)) {
+ DVLOG(1) << "Implicit internal passphrase accepted for decryption.";
+ cryptographer->GetBootstrapToken(bootstrap_token);
+ return true;
+ } else {
+ DVLOG(1) << "Implicit internal passphrase failed to decrypt, adding "
+ << "anyways as default passphrase and persisting via "
+ << "bootstrap token.";
+ // Turns out we're encrypted with an old GAIA password, and we're
+ // actually in case 3. But, because this is the current GAIA
+ // password, we need to generate a new bootstrap token to preserve it.
+ // We build a temporary cryptographer to allow us to extract these
+ // params without polluting our current cryptographer.
+ Cryptographer temp_cryptographer;
+ temp_cryptographer.AddKey(key_params);
+ temp_cryptographer.GetBootstrapToken(bootstrap_token);
+ // We then set the new passphrase as the default passphrase of the
+ // real cryptographer, even though we have pending keys. This is safe,
+ // as although Cryptographer::is_initialized() will now be true,
+ // is_ready() will remain false due to having pending keys.
+ cryptographer->AddKey(key_params);
+ return false;
+ }
+ } else { // user_provided == true
+ if (cryptographer->is_initialized()) {
+ // We only want to change the default encryption key to the pending
+ // one if the pending keybag already contains the current default.
+ // This covers the case where a different client re-encrypted
+ // everything with a newer gaia passphrase (and hence the keybag
+ // contains keys from all previously used gaia passphrases).
+ // Otherwise, we're in a situation where the pending keys are
+ // encrypted with an old gaia passphrase, while the default is the
+ // current gaia passphrase. In that case, we preserve the default.
+ Cryptographer temp_cryptographer;
+ temp_cryptographer.SetPendingKeys(cryptographer->GetPendingKeys());
+ if (temp_cryptographer.DecryptPendingKeys(key_params)) {
+ // Check to see if the pending bag of keys contains the current
+ // default key.
+ sync_pb::EncryptedData encrypted;
+ cryptographer->GetKeys(&encrypted);
+ if (temp_cryptographer.CanDecrypt(encrypted)) {
+ DVLOG(1) << "Implicit user provided passphrase accepted for "
+ << "decryption, overwriting default.";
+ // The pending keybag contains the current default. Go ahead
+ // and update the cryptographer, letting the default change.
+ // Case 3.
+ cryptographer->DecryptPendingKeys(key_params);
+ cryptographer->GetBootstrapToken(bootstrap_token);
+ return true;
+ } else {
+ // The pending keybag does not contain the current default
+ // encryption key. We want to restore the current default
+ // after decrypting the pending keys.
+ // Case 4.
+ DVLOG(1) << "Implicit user provided passphrase accepted for "
+ << "decryption, restoring implicit internal passphrase "
+ << "as default.";
+ std::string bootstrap_token_from_current_key;
+ cryptographer->GetBootstrapToken(
+ &bootstrap_token_from_current_key);
+ cryptographer->DecryptPendingKeys(key_params);
+ // Overwrite the default from the pending keys.
+ cryptographer->AddKeyFromBootstrapToken(
+ bootstrap_token_from_current_key);
+ return true;
+ }
+ }
+ } else if (cryptographer->DecryptPendingKeys(key_params)) {
+ // This can happpen if this is a client that has lost the credentials
+ // from the current gaia password, and has data encrypted with an old
+ // gaia password. We won't be able to re-encrypt to the most recent
+ // GAIA password, so for now just continue and initialize the
+ // cryptographer.
+ // This is a subset of case 4 that we don't handle properly yet.
+ // TODO(zea): trigger prompting for re-auth here. See part 2 of
+ // http://crbug.com/104508.
+ DVLOG(1) << "Implicit user provided passphrase accepted, initializing"
+ << " cryptographer.";
+ return true;
+ } else {
+ DVLOG(1) << "Implicit user provided passphrase failed to decrypt.";
+ return false;
+ }
+ } // user_provided
+ } else { // is_explicit == true
+ // This can happen if the client changes their password, re-authed on
+ // another machine, and we only just now received the updated nigori.
+ DVLOG(1) << "Explicit passphrase failed to decrypt because nigori had "
+ << "implicit passphrase.";
+ return false;
+ } // is_explicit
+ } else { // nigori_has_explicit_passphrase == true
+ if (!is_explicit) {
+ DVLOG(1) << "Implicit passphrase rejected because nigori had explicit "
+ << "passphrase.";
+ return false;
+ } else { // is_explicit == true
+ // Case 5.
+ if (cryptographer->DecryptPendingKeys(key_params)) {
+ DVLOG(1) << "Explicit passphrase accepted for decryption.";
+ cryptographer->GetBootstrapToken(bootstrap_token);
+ return true;
+ } else {
+ DVLOG(1) << "Explicit passphrase failed to decrypt.";
+ return false;
+ }
+ } // is_explicit
+ } // nigori_has_explicit_passphrase
+ NOTREACHED();
+ return false;
}
bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() {
@@ -1389,8 +1612,7 @@ void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) {
}
// NOTE: We notify from within a transaction.
- FOR_EACH_OBSERVER(
- SyncManager::Observer, observers_, OnEncryptionComplete());
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_, OnEncryptionComplete());
}
SyncManager::~SyncManager() {
diff --git a/chrome/browser/sync/internal_api/sync_manager.h b/chrome/browser/sync/internal_api/sync_manager.h
index f0ece70..cfb466c 100644
--- a/chrome/browser/sync/internal_api/sync_manager.h
+++ b/chrome/browser/sync/internal_api/sync_manager.h
@@ -285,11 +285,19 @@ class SyncManager {
const sync_pb::EncryptedData& pending_keys) = 0;
// Called when the passphrase provided by the user has been accepted and is
- // now used to encrypt sync data. |bootstrap_token| is an opaque base64
- // encoded representation of the key generated by the accepted passphrase,
- // and is provided to the observer for persistence purposes and use in a
- // future initialization of sync (e.g. after restart).
- virtual void OnPassphraseAccepted(const std::string& bootstrap_token) = 0;
+ // now used to encrypt sync data.
+ virtual void OnPassphraseAccepted() = 0;
+
+ // |bootstrap_token| is an opaque base64 encoded representation of the key
+ // generated by the current passphrase, and is provided to the observer for
+ // persistence purposes and use in a future initialization of sync (e.g.
+ // after restart). The boostrap token will always be derived from the most
+ // recent GAIA password (for accounts with implicit passphrases), even if
+ // the data is still encrypted with an older GAIA password. For accounts
+ // with explicit passphrases, it will be the most recently seen custom
+ // passphrase.
+ virtual void OnBootstrapTokenUpdated(
+ const std::string& bootstrap_token) = 0;
// Called when initialization is complete to the point that SyncManager can
// process changes. This does not necessarily mean authentication succeeded
@@ -479,11 +487,18 @@ class SyncManager {
// If the passphrase in invalid, OnPassphraseRequired will be fired.
// Calling this metdod again is the appropriate course of action to "retry"
// with a new passphrase.
- // |is_explicit| is true if the call is in response to the user explicitly
- // setting a passphrase as opposed to implicitly (from the users' perspective)
- // using their Google Account password. An implicit SetPassphrase will *not*
- // *not* override an explicit passphrase set previously.
- void SetPassphrase(const std::string& passphrase, bool is_explicit);
+ // |is_explicit| is true if the call is in response to the user setting a
+ // custom explicit passphrase as opposed to implicitly (from the users'
+ // perspective) using their Google Account password. An implicit SetPassphrase
+ // will *not* override an explicit passphrase set previously. Note that
+ // if the data is encrypted with an old Google Account password, the user
+ // may still have to provide a "implicit" passphrase.
+ // |user_provided| corresponds to the user having manually provided this
+ // passphrase. It should only be false for passphrases intercepted from the
+ // Google Sign-in Success notification and true otherwise.
+ void SetPassphrase(const std::string& passphrase,
+ bool is_explicit,
+ bool user_provided);
// Puts the SyncScheduler into a mode where no normal nudge or poll traffic
// will occur, but calls to RequestConfig will be supported. If |callback|
diff --git a/chrome/browser/sync/internal_api/syncapi_unittest.cc b/chrome/browser/sync/internal_api/syncapi_unittest.cc
index 0fb6e4a..c0b520d 100644
--- a/chrome/browser/sync/internal_api/syncapi_unittest.cc
+++ b/chrome/browser/sync/internal_api/syncapi_unittest.cc
@@ -66,6 +66,7 @@ using browser_sync::Cryptographer;
using browser_sync::HasArgsAsList;
using browser_sync::HasDetailsAsDictionary;
using browser_sync::KeyParams;
+using browser_sync::kNigoriTag;
using browser_sync::JsArgList;
using browser_sync::JsBackend;
using browser_sync::JsEventHandler;
@@ -667,7 +668,8 @@ class SyncManagerObserverMock : public SyncManager::Observer {
MOCK_METHOD2(OnPassphraseRequired,
void(sync_api::PassphraseRequiredReason,
const sync_pb::EncryptedData&)); // NOLINT
- MOCK_METHOD1(OnPassphraseAccepted, void(const std::string&)); // NOLINT
+ MOCK_METHOD0(OnPassphraseAccepted, void()); // NOLINT
+ MOCK_METHOD1(OnBootstrapTokenUpdated, void(const std::string&)); // NOLINT
MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT
MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT
MOCK_METHOD0(OnClearServerDataFailed, void()); // NOLINT
@@ -707,6 +709,7 @@ class SyncManagerTest : public testing::Test,
};
enum EncryptionStatus {
+ UNINITIALIZED,
DEFAULT_ENCRYPTION,
FULL_ENCRYPTION
};
@@ -833,8 +836,12 @@ class SyncManagerTest : public testing::Test,
Cryptographer* cryptographer = trans.GetCryptographer();
if (!cryptographer)
return false;
- KeyParams params = {"localhost", "dummy", "foobar"};
- cryptographer->AddKey(params);
+ if (encryption_status != UNINITIALIZED) {
+ KeyParams params = {"localhost", "dummy", "foobar"};
+ cryptographer->AddKey(params);
+ } else {
+ DCHECK_NE(nigori_status, WRITE_TO_NIGORI);
+ }
if (encryption_status == FULL_ENCRYPTION)
cryptographer->set_encrypt_everything();
if (nigori_status == WRITE_TO_NIGORI) {
@@ -1448,9 +1455,10 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
// Trigger's a ReEncryptEverything with new passphrase.
testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnPassphraseAccepted(_));
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetPassphrase("new_passphrase", true);
+ sync_manager_.SetPassphrase("new_passphrase", true, true);
EXPECT_TRUE(sync_manager_.EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1476,15 +1484,76 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
// a reencryption and should just notify immediately.
// TODO(zea): add logic to ensure nothing was written.
testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnPassphraseAccepted(_)).Times(0);
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_)).Times(0);
+ EXPECT_CALL(observer_, OnPassphraseAccepted()).Times(0);
EXPECT_CALL(observer_, OnEncryptionComplete());
sync_manager_.EnableEncryptEverything();
}
+// Test that when there are no pending keys and the cryptographer is not
+// initialized, we add a key based on the current GAIA password.
+// (case 1 in SyncManager::SyncInternalSetPassphrase)
+TEST_F(SyncManagerTest, SetInitialGaiaPass) {
+ EXPECT_FALSE(SetUpEncryption(DONT_WRITE_NIGORI, UNINITIALIZED));
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
+ EXPECT_CALL(observer_, OnEncryptionComplete());
+ sync_manager_.SetPassphrase("new_passphrase", false, false);
+ EXPECT_FALSE(sync_manager_.EncryptEverythingEnabledForTest());
+ {
+ ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ ReadNode node(&trans);
+ EXPECT_TRUE(node.InitByTagLookup(kNigoriTag));
+ sync_pb::NigoriSpecifics nigori = node.GetNigoriSpecifics();
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ EXPECT_TRUE(cryptographer->is_ready());
+ EXPECT_TRUE(cryptographer->CanDecrypt(nigori.encrypted()));
+ }
+}
+
+// Test that when there are no pending keys and we have on the old GAIA
+// password, we update and re-encrypt everything with the new GAIA password.
+// (case 1 in SyncManager::SyncInternalSetPassphrase)
+TEST_F(SyncManagerTest, UpdateGaiaPass) {
+ EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
+ Cryptographer verifier;
+ {
+ ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ std::string bootstrap_token;
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ verifier.Bootstrap(bootstrap_token);
+ }
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
+ EXPECT_CALL(observer_, OnEncryptionComplete());
+ sync_manager_.SetPassphrase("new_passphrase", false, false);
+ EXPECT_FALSE(sync_manager_.EncryptEverythingEnabledForTest());
+ {
+ ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ EXPECT_TRUE(cryptographer->is_ready());
+ // Verify the default key has changed.
+ sync_pb::EncryptedData encrypted;
+ cryptographer->GetKeys(&encrypted);
+ EXPECT_FALSE(verifier.CanDecrypt(encrypted));
+ }
+}
+
+// Sets a new explicit passphrase. This should update the bootstrap token
+// and re-encrypt everything.
+// (case 2 in SyncManager::SyncInternalSetPassphrase)
TEST_F(SyncManagerTest, SetPassphraseWithPassword) {
+ Cryptographer verifier;
EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
{
WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ // Store the default (soon to be old) key.
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ std::string bootstrap_token;
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ verifier.Bootstrap(bootstrap_token);
+
ReadNode root_node(&trans);
root_node.InitByRootLookup();
@@ -1495,12 +1564,20 @@ TEST_F(SyncManagerTest, SetPassphraseWithPassword) {
data.set_password_value("secret");
password_node.SetPasswordSpecifics(data);
}
- EXPECT_CALL(observer_, OnPassphraseAccepted(_));
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetPassphrase("new_passphrase", true);
+ sync_manager_.SetPassphrase("new_passphrase", true, true);
EXPECT_FALSE(sync_manager_.EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ EXPECT_TRUE(cryptographer->is_ready());
+ // Verify the default key has changed.
+ sync_pb::EncryptedData encrypted;
+ cryptographer->GetKeys(&encrypted);
+ EXPECT_FALSE(verifier.CanDecrypt(encrypted));
+
ReadNode password_node(&trans);
EXPECT_TRUE(password_node.InitByClientTagLookup(syncable::PASSWORDS,
"foo"));
@@ -1510,6 +1587,163 @@ TEST_F(SyncManagerTest, SetPassphraseWithPassword) {
}
}
+// Manually set the pending keys in the cryptographer/nigori to reflect the data
+// being encrypted with a new (unprovided) GAIA password, then supply the
+// password.
+// (case 3 in SyncManager::SyncInternalSetPassphrase)
+TEST_F(SyncManagerTest, SupplyPendingGAIAPass) {
+ EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
+ Cryptographer other_cryptographer;
+ {
+ WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ std::string bootstrap_token;
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ other_cryptographer.Bootstrap(bootstrap_token);
+
+ // Now update the nigori to reflect the new keys, and update the
+ // cryptographer to have pending keys.
+ KeyParams params = {"localhost", "dummy", "passphrase2"};
+ other_cryptographer.AddKey(params);
+ WriteNode node(&trans);
+ EXPECT_TRUE(node.InitByTagLookup(kNigoriTag));
+ sync_pb::NigoriSpecifics nigori;
+ other_cryptographer.GetKeys(nigori.mutable_encrypted());
+ cryptographer->Update(nigori);
+ EXPECT_TRUE(cryptographer->has_pending_keys());
+ node.SetNigoriSpecifics(nigori);
+ }
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
+ EXPECT_CALL(observer_, OnEncryptionComplete());
+ sync_manager_.SetPassphrase("passphrase2", false, false);
+ EXPECT_FALSE(sync_manager_.EncryptEverythingEnabledForTest());
+ {
+ ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ EXPECT_TRUE(cryptographer->is_ready());
+ // Verify we're encrypting with the new key.
+ sync_pb::EncryptedData encrypted;
+ cryptographer->GetKeys(&encrypted);
+ EXPECT_TRUE(other_cryptographer.CanDecrypt(encrypted));
+ }
+}
+
+// Manually set the pending keys in the cryptographer/nigori to reflect the data
+// being encrypted with an old (unprovided) GAIA password. Attempt to supply
+// the current GAIA password and verify the bootstrap token is updated. Then
+// supply the old GAIA password, and verify we re-encrypt all data with the
+// new GAIA password.
+// (case 4 in SyncManager::SyncInternalSetPassphrase)
+TEST_F(SyncManagerTest, SupplyPendingOldGAIAPass) {
+ EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
+ Cryptographer other_cryptographer;
+ {
+ WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ std::string bootstrap_token;
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ other_cryptographer.Bootstrap(bootstrap_token);
+
+ // Now update the nigori to reflect the new keys, and update the
+ // cryptographer to have pending keys.
+ KeyParams params = {"localhost", "dummy", "old_gaia"};
+ other_cryptographer.AddKey(params);
+ WriteNode node(&trans);
+ EXPECT_TRUE(node.InitByTagLookup(kNigoriTag));
+ sync_pb::NigoriSpecifics nigori;
+ other_cryptographer.GetKeys(nigori.mutable_encrypted());
+ node.SetNigoriSpecifics(nigori);
+ cryptographer->Update(nigori);
+
+ // other_cryptographer now contains all encryption keys, and is encrypting
+ // with the newest gaia.
+ KeyParams new_params = {"localhost", "dummy", "new_gaia"};
+ other_cryptographer.AddKey(new_params);
+ }
+ // The bootstrap token should have been updated. Save it to ensure it's based
+ // on the new GAIA password.
+ std::string bootstrap_token;
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_))
+ .WillOnce(SaveArg<0>(&bootstrap_token));
+ EXPECT_CALL(observer_, OnPassphraseRequired(_,_));
+ sync_manager_.SetPassphrase("new_gaia", false, false);
+ EXPECT_FALSE(sync_manager_.EncryptEverythingEnabledForTest());
+ testing::Mock::VerifyAndClearExpectations(&observer_);
+ {
+ ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ EXPECT_TRUE(cryptographer->is_initialized());
+ EXPECT_FALSE(cryptographer->is_ready());
+ // Verify we're encrypting with the new key, even though we have pending
+ // keys.
+ sync_pb::EncryptedData encrypted;
+ other_cryptographer.GetKeys(&encrypted);
+ EXPECT_TRUE(cryptographer->CanDecrypt(encrypted));
+ }
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
+ EXPECT_CALL(observer_, OnEncryptionComplete());
+ sync_manager_.SetPassphrase("old_gaia", false, true);
+ {
+ ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ EXPECT_TRUE(cryptographer->is_ready());
+
+ // Verify we're encrypting with the new key.
+ sync_pb::EncryptedData encrypted;
+ other_cryptographer.GetKeys(&encrypted);
+ EXPECT_TRUE(cryptographer->CanDecrypt(encrypted));
+
+ // Verify the saved bootstrap token is based on the new gaia password.
+ Cryptographer temp_cryptographer;
+ temp_cryptographer.Bootstrap(bootstrap_token);
+ EXPECT_TRUE(temp_cryptographer.CanDecrypt(encrypted));
+ }
+}
+
+// Manually set the pending keys in the cryptographer/nigori to reflect the data
+// being encrypted with an explicit (unprovided) passphrase, then supply the
+// passphrase.
+// (case 5 in SyncManager::SyncInternalSetPassphrase)
+TEST_F(SyncManagerTest, SupplyPendingExplicitPass) {
+ EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
+ Cryptographer other_cryptographer;
+ {
+ WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ std::string bootstrap_token;
+ cryptographer->GetBootstrapToken(&bootstrap_token);
+ other_cryptographer.Bootstrap(bootstrap_token);
+
+ // Now update the nigori to reflect the new keys, and update the
+ // cryptographer to have pending keys.
+ KeyParams params = {"localhost", "dummy", "explicit"};
+ other_cryptographer.AddKey(params);
+ WriteNode node(&trans);
+ EXPECT_TRUE(node.InitByTagLookup(kNigoriTag));
+ sync_pb::NigoriSpecifics nigori;
+ other_cryptographer.GetKeys(nigori.mutable_encrypted());
+ cryptographer->Update(nigori);
+ EXPECT_TRUE(cryptographer->has_pending_keys());
+ nigori.set_using_explicit_passphrase(true);
+ node.SetNigoriSpecifics(nigori);
+ }
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
+ EXPECT_CALL(observer_, OnEncryptionComplete());
+ sync_manager_.SetPassphrase("explicit", true, true);
+ EXPECT_FALSE(sync_manager_.EncryptEverythingEnabledForTest());
+ {
+ ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ EXPECT_TRUE(cryptographer->is_ready());
+ // Verify we're encrypting with the new key.
+ sync_pb::EncryptedData encrypted;
+ cryptographer->GetKeys(&encrypted);
+ EXPECT_TRUE(other_cryptographer.CanDecrypt(encrypted));
+ }
+}
+
TEST_F(SyncManagerTest, SetPassphraseWithEmptyPasswordNode) {
EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
int64 node_id = 0;
@@ -1524,9 +1758,10 @@ TEST_F(SyncManagerTest, SetPassphraseWithEmptyPasswordNode) {
root_node, tag));
node_id = password_node.GetId();
}
- EXPECT_CALL(observer_, OnPassphraseAccepted(_));
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetPassphrase("new_passphrase", true);
+ sync_manager_.SetPassphrase("new_passphrase", true, true);
EXPECT_FALSE(sync_manager_.EncryptEverythingEnabledForTest());
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1715,9 +1950,10 @@ TEST_F(SyncManagerTest, UpdateEntryWithEncryption) {
// Set a new passphrase. Should set is_unsynced.
testing::Mock::VerifyAndClearExpectations(&observer_);
- EXPECT_CALL(observer_, OnPassphraseAccepted(_));
+ EXPECT_CALL(observer_, OnBootstrapTokenUpdated(_));
+ EXPECT_CALL(observer_, OnPassphraseAccepted());
EXPECT_CALL(observer_, OnEncryptionComplete());
- sync_manager_.SetPassphrase("new_passphrase", true);
+ sync_manager_.SetPassphrase("new_passphrase", true, true);
{
ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
ReadNode node(&trans);
diff --git a/chrome/browser/sync/js/js_sync_manager_observer.cc b/chrome/browser/sync/js/js_sync_manager_observer.cc
index ecb4304..843ac1d 100644
--- a/chrome/browser/sync/js/js_sync_manager_observer.cc
+++ b/chrome/browser/sync/js/js_sync_manager_observer.cc
@@ -71,16 +71,24 @@ void JsSyncManagerObserver::OnPassphraseRequired(
HandleJsEvent(FROM_HERE, "onPassphraseRequired", JsEventDetails(&details));
}
-void JsSyncManagerObserver::OnPassphraseAccepted(
- const std::string& bootstrap_token) {
+void JsSyncManagerObserver::OnPassphraseAccepted() {
if (!event_handler_.IsInitialized()) {
return;
}
DictionaryValue details;
- details.SetString("bootstrapToken", "<redacted>");
HandleJsEvent(FROM_HERE, "onPassphraseAccepted", JsEventDetails(&details));
}
+void JsSyncManagerObserver::OnBootstrapTokenUpdated(
+ const std::string& boostrap_token) {
+ if (!event_handler_.IsInitialized()) {
+ return;
+ }
+ DictionaryValue details;
+ details.SetString("bootstrapToken", "<redacted>");
+ HandleJsEvent(FROM_HERE, "OnBootstrapTokenUpdated", JsEventDetails(&details));
+}
+
void JsSyncManagerObserver::OnEncryptedTypesChanged(
syncable::ModelTypeSet encrypted_types,
bool encrypt_everything) {
diff --git a/chrome/browser/sync/js/js_sync_manager_observer.h b/chrome/browser/sync/js/js_sync_manager_observer.h
index 44d2b5c..e3cddc7 100644
--- a/chrome/browser/sync/js/js_sync_manager_observer.h
+++ b/chrome/browser/sync/js/js_sync_manager_observer.h
@@ -39,7 +39,8 @@ class JsSyncManagerObserver : public sync_api::SyncManager::Observer {
virtual void OnPassphraseRequired(
sync_api::PassphraseRequiredReason reason,
const sync_pb::EncryptedData& pending_keys) OVERRIDE;
- virtual void OnPassphraseAccepted(
+ virtual void OnPassphraseAccepted() OVERRIDE;
+ virtual void OnBootstrapTokenUpdated(
const std::string& bootstrap_token) OVERRIDE;
virtual void OnEncryptedTypesChanged(
syncable::ModelTypeSet encrypted_types,
diff --git a/chrome/browser/sync/js/js_sync_manager_observer_unittest.cc b/chrome/browser/sync/js/js_sync_manager_observer_unittest.cc
index be15219..1145458 100644
--- a/chrome/browser/sync/js/js_sync_manager_observer_unittest.cc
+++ b/chrome/browser/sync/js/js_sync_manager_observer_unittest.cc
@@ -191,11 +191,11 @@ TEST_F(JsSyncManagerObserverTest, SensitiveNotifiations) {
HasDetailsAsDictionary(redacted_token_details)));
EXPECT_CALL(mock_js_event_handler_,
HandleJsEvent(
- "onPassphraseAccepted",
+ "OnBootstrapTokenUpdated",
HasDetailsAsDictionary(redacted_bootstrap_token_details)));
js_sync_manager_observer_.OnUpdatedToken("sensitive_token");
- js_sync_manager_observer_.OnPassphraseAccepted("sensitive_token");
+ js_sync_manager_observer_.OnBootstrapTokenUpdated("sensitive_token");
PumpLoop();
}
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 111c72b..20d6572 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -810,7 +810,9 @@ void ProfileSyncService::OnPassphraseRequired(
cached_passphrases_.gaia_passphrase.clear();
DVLOG(1) << "Attempting gaia passphrase.";
// SetPassphrase will re-cache this passphrase if the syncer isn't ready.
- SetPassphrase(gaia_passphrase, false);
+ SetPassphrase(gaia_passphrase, IMPLICIT,
+ (cached_passphrases_.user_provided_gaia ?
+ USER_PROVIDED : INTERNAL));
return;
}
@@ -821,7 +823,7 @@ void ProfileSyncService::OnPassphraseRequired(
cached_passphrases_.explicit_passphrase.clear();
DVLOG(1) << "Attempting explicit passphrase.";
// SetPassphrase will re-cache this passphrase if the syncer isn't ready.
- SetPassphrase(explicit_passphrase, true);
+ SetPassphrase(explicit_passphrase, EXPLICIT, USER_PROVIDED);
return;
}
@@ -1288,15 +1290,27 @@ void ProfileSyncService::DeactivateDataType(syncable::ModelType type) {
}
void ProfileSyncService::SetPassphrase(const std::string& passphrase,
- bool is_explicit) {
+ PassphraseType type,
+ PassphraseSource source) {
+ DCHECK(source == USER_PROVIDED || type == IMPLICIT);
if (ShouldPushChanges() || IsPassphraseRequired()) {
- DVLOG(1) << "Setting " << (is_explicit ? "explicit" : "implicit");
- backend_->SetPassphrase(passphrase, is_explicit);
+ DVLOG(1) << "Setting " << (type == EXPLICIT ? "explicit" : "implicit")
+ << " passphrase.";
+ backend_->SetPassphrase(passphrase,
+ type == EXPLICIT,
+ source == USER_PROVIDED);
} else {
- if (is_explicit) {
+ if (type == EXPLICIT) {
+ NOTREACHED() << "SetPassphrase should only be called after the backend "
+ << "is initialized.";
cached_passphrases_.explicit_passphrase = passphrase;
} else {
cached_passphrases_.gaia_passphrase = passphrase;
+ cached_passphrases_.user_provided_gaia = (source == USER_PROVIDED);
+ DVLOG(1) << "Caching "
+ << (cached_passphrases_.user_provided_gaia ? "user provided" :
+ "internal")
+ << " gaia passphrase.";
}
}
}
@@ -1437,13 +1451,12 @@ void ProfileSyncService::Observe(int type,
// want to suppress start up anymore.
sync_prefs_.SetStartSuppressed(false);
- // We pass 'false' to SetPassphrase to denote that this is an implicit
- // request and shouldn't override an explicit one. Thus, we either
- // update the implicit passphrase (idempotent if the passphrase didn't
- // actually change), or the user has an explicit passphrase set so this
- // becomes a no-op.
+ // Because we specify IMPLICIT to SetPassphrase, we know it won't override
+ // an explicit one. Thus, we either update the implicit passphrase
+ // (idempotent if the passphrase didn't actually change), or the user has
+ // an explicit passphrase set so this becomes a no-op.
if (!successful->password.empty())
- SetPassphrase(successful->password, false);
+ SetPassphrase(successful->password, IMPLICIT, INTERNAL);
break;
}
case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED: {
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index d9d8068..c05d70b 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -155,6 +155,16 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
MANUAL_START,
};
+ enum PassphraseType {
+ IMPLICIT,
+ EXPLICIT,
+ };
+
+ enum PassphraseSource {
+ INTERNAL,
+ USER_PROVIDED,
+ };
+
// Default sync server URL.
static const char* kSyncServerUrl;
// Sync server URL for dev channel users
@@ -448,12 +458,19 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// This will check asynchronously whether the passphrase is valid and notify
// ProfileSyncServiceObservers via the NotificationService when the outcome
// is known.
- // |is_explicit| is true if the call is in response to the user explicitly
- // setting a passphrase as opposed to implicitly (from the users' perspective)
- // using their Google Account password. An implicit SetPassphrase will
- // *not* override an explicit passphrase set previously.
+ // |type| == EXPLICIT if the call is in response to the user setting a
+ // custom explicit passphrase as opposed to implicitly (from the users'
+ // perspective) using their Google Account password. Once an explicit
+ // passphrase is set, it can never be overwritten (not even by another
+ // explicit passphrase).
+ // |source| == USER_PROVIDED corresponds to the user having manually provided
+ // this passphrase. It should only be INTERNAL for passphrases intercepted
+ // from the Google Sign-in Success notification. Note that if the data is
+ // encrypted with an old Google Account password, the user may still have to
+ // provide an "implicit" passphrase.
virtual void SetPassphrase(const std::string& passphrase,
- bool is_explicit);
+ PassphraseType type,
+ PassphraseSource source);
// Turns on encryption for all data. Callers must call OnUserChoseDatatypes()
// after calling this to force the encryption to occur.
@@ -672,6 +689,9 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
struct CachedPassphrases {
std::string explicit_passphrase;
std::string gaia_passphrase;
+ // This distinguishes from GAIA passphrases intercepted by the signin code
+ // (which will always be the most recent GAIA passphrase).
+ bool user_provided_gaia;
};
CachedPassphrases cached_passphrases_;
diff --git a/chrome/browser/sync/profile_sync_service_harness.cc b/chrome/browser/sync/profile_sync_service_harness.cc
index 7a999e7..59c039a 100644
--- a/chrome/browser/sync/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/profile_sync_service_harness.cc
@@ -193,7 +193,9 @@ bool ProfileSyncServiceHarness::SetupSync(
}
// Set our implicit passphrase.
- service_->SetPassphrase(password_, false);
+ service_->SetPassphrase(password_,
+ ProfileSyncService::IMPLICIT,
+ ProfileSyncService::USER_PROVIDED);
// Wait for initial sync cycle to be completed.
DCHECK_EQ(wait_state_, WAITING_FOR_INITIAL_SYNC);
diff --git a/chrome/browser/sync/profile_sync_service_password_unittest.cc b/chrome/browser/sync/profile_sync_service_password_unittest.cc
index 0ccfbba..0b2bbf0 100644
--- a/chrome/browser/sync/profile_sync_service_password_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc
@@ -267,7 +267,9 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest {
MessageLoop::current()->Run();
FlushLastDBTask();
- service_->SetPassphrase("foo", false);
+ service_->SetPassphrase("foo",
+ ProfileSyncService::IMPLICIT,
+ ProfileSyncService::INTERNAL);
MessageLoop::current()->Run();
}
}
diff --git a/chrome/browser/sync/protocol/client_debug_info.proto b/chrome/browser/sync/protocol/client_debug_info.proto
index af97839..6d67b72 100644
--- a/chrome/browser/sync/protocol/client_debug_info.proto
+++ b/chrome/browser/sync/protocol/client_debug_info.proto
@@ -38,6 +38,8 @@ message DebugEventInfo {
ENCRYPTED_TYPES_CHANGED = 9; // Set of encrypted types has changed.
ENCRYPTION_COMPLETE = 7; // Client has finished encrypting all data.
ACTIONABLE_ERROR = 8; // Client received an actionable error.
+ BOOTSTRAP_TOKEN_UPDATED = 9; // A new cryptographer bootstrap token was
+ // generated.
}
optional EventType type = 1;
optional SyncCycleCompletedEventInfo sync_cycle_completed_event_info = 2;
diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc
index 3e2f1f2..c1abbee 100644
--- a/chrome/browser/sync/sync_setup_flow.cc
+++ b/chrome/browser/sync/sync_setup_flow.cc
@@ -453,7 +453,9 @@ void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
// Caller passed a gaia passphrase. This is illegal if we are currently
// using a secondary passphrase.
DCHECK(!service_->IsUsingSecondaryPassphrase());
- service_->SetPassphrase(configuration.gaia_passphrase, false);
+ service_->SetPassphrase(configuration.gaia_passphrase,
+ ProfileSyncService::IMPLICIT,
+ ProfileSyncService::USER_PROVIDED);
// Since the user entered the passphrase manually, set this flag so we can
// report an error if the passphrase setting failed.
user_tried_setting_passphrase_ = true;
@@ -464,7 +466,9 @@ void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
// as an attempt to encrypt the user's data using this new passphrase.
if (configuration.set_secondary_passphrase &&
!configuration.secondary_passphrase.empty()) {
- service_->SetPassphrase(configuration.secondary_passphrase, true);
+ service_->SetPassphrase(configuration.secondary_passphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
if (service_->IsUsingSecondaryPassphrase()) {
user_tried_setting_passphrase_ = true;
set_new_decryption_passphrase = true;
@@ -494,7 +498,9 @@ void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase) {
Advance(SyncSetupWizard::SETTING_UP);
- service_->SetPassphrase(passphrase, true);
+ service_->SetPassphrase(passphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
user_tried_setting_passphrase_ = true;
}
diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc
index 46b7cb0..5f0f98f 100644
--- a/chrome/browser/sync/sync_setup_wizard_unittest.cc
+++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc
@@ -114,7 +114,8 @@ class ProfileSyncServiceForWizardTest : public ProfileSyncService {
}
virtual void SetPassphrase(const std::string& passphrase,
- bool is_explicit) OVERRIDE {
+ PassphraseType type,
+ PassphraseSource source) OVERRIDE {
passphrase_ = passphrase;
}
diff --git a/chrome/browser/sync/test/integration/passwords_helper.cc b/chrome/browser/sync/test/integration/passwords_helper.cc
index 947ac47..f2cf8ab 100644
--- a/chrome/browser/sync/test/integration/passwords_helper.cc
+++ b/chrome/browser/sync/test/integration/passwords_helper.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -107,7 +107,9 @@ void RemoveLogins(PasswordStore* store) {
void SetPassphrase(int index, const std::string& passphrase) {
test()->GetProfile(index)->GetProfileSyncService()->SetPassphrase(
- passphrase, true);
+ passphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
}
PasswordStore* GetPasswordStore(int index) {
diff --git a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
index 450e6713..808b1a2 100644
--- a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
@@ -1804,7 +1804,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
// Set a passphrase and enable encryption on Client 0. Client 1 will not
// understand the bookmark updates.
- GetClient(0)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(0)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
ASSERT_TRUE(EnableEncryption(0, syncable::BOOKMARKS));
ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
@@ -1821,7 +1824,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
EXPECT_FALSE(AllModelsMatch());
// Set the passphrase. Everything should resolve.
- GetClient(1)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(1)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
EXPECT_TRUE(AllModelsMatchVerifier());
diff --git a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
index 9b62e2f..e47084d 100644
--- a/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_passwords_sync_test.cc
@@ -243,22 +243,6 @@ IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
ASSERT_EQ(browser_sync::GROUP_PASSWORD, routes[syncable::PASSWORDS]);
}
-IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest, SetPassphraseTwice) {
- ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
-
- SetPassphrase(0, kValidPassphrase);
- ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
- ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
-
- SetPassphrase(1, kValidPassphrase);
- ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
- ASSERT_TRUE(GetClient(1)->AwaitFullSyncCompletion("Set passphrase."));
-
- SetPassphrase(1, kValidPassphrase);
- ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
- ASSERT_TRUE(GetClient(1)->AwaitFullSyncCompletion("Set passphrase again."));
-}
-
IN_PROC_BROWSER_TEST_F(TwoClientPasswordsSyncTest,
SetDifferentPassphraseAndThenSetupSync) {
ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
diff --git a/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc
index d5c9a23..0134830 100644
--- a/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc
@@ -160,7 +160,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
client0_windows.GetMutable()));
ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
- GetClient(0)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(0)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
@@ -172,7 +175,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
GetClient(1)->GetLastSessionSnapshot()->
num_conflicting_updates); // The encrypted nodes.
- GetClient(1)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(1)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
@@ -204,7 +210,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
ASSERT_TRUE(CheckInitialState(1));
ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
- GetClient(0)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(0)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
@@ -226,7 +235,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
GetClient(1)->GetLastSessionSnapshot()->
num_conflicting_updates); // The encrypted nodes.
- GetClient(1)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(1)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
@@ -250,7 +262,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
ASSERT_TRUE(CheckInitialState(1));
ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
- GetClient(0)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(0)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
@@ -270,7 +285,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
// At this point we enter the passphrase, triggering a resync, in which the
// local changes of client 1 get sent to client 0.
- GetClient(1)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(1)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0)));
@@ -309,7 +327,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
// Turn encryption on client 0. Client 1's foreign will be encrypted with the
// new passphrase and synced back. It will be unable to decrypt it yet.
ASSERT_TRUE(EnableEncryption(0, syncable::SESSIONS));
- GetClient(0)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(0)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
ASSERT_TRUE(AwaitQuiescence());
ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
@@ -323,7 +344,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
num_conflicting_updates); // The encrypted nodes.
// At this point we enter the passphrase, triggering a resync.
- GetClient(1)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(1)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
@@ -354,7 +378,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
ASSERT_TRUE(CheckInitialState(0));
ASSERT_TRUE(CheckInitialState(1));
- GetClient(0)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(0)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted());
ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
ASSERT_TRUE(GetClient(1)->AwaitPassphraseRequired());
@@ -375,7 +402,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest,
GetClient(1)->GetLastSessionSnapshot()->
num_conflicting_updates); // The encrypted nodes.
- GetClient(1)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(1)->service()->SetPassphrase(
+ kValidPassphrase,
+ ProfileSyncService::EXPLICIT,
+ ProfileSyncService::USER_PROVIDED);
ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted());
ASSERT_FALSE(GetClient(1)->service()->IsPassphraseRequired());
ASSERT_TRUE(GetClient(1)->WaitForTypeEncryption(syncable::SESSIONS));
diff --git a/chrome/browser/sync/util/cryptographer.cc b/chrome/browser/sync/util/cryptographer.cc
index de83652..26fbc55 100644
--- a/chrome/browser/sync/util/cryptographer.cc
+++ b/chrome/browser/sync/util/cryptographer.cc
@@ -131,8 +131,6 @@ bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const {
}
bool Cryptographer::AddKey(const KeyParams& params) {
- DCHECK(NULL == pending_keys_.get());
-
// Create the new Nigori and make it the default encryptor.
scoped_ptr<Nigori> nigori(new Nigori);
if (!nigori->InitByDerivation(params.hostname,
@@ -144,6 +142,15 @@ bool Cryptographer::AddKey(const KeyParams& params) {
return AddKeyImpl(nigori.release());
}
+bool Cryptographer::AddKeyFromBootstrapToken(
+ const std::string restored_bootstrap_token) {
+ // Create the new Nigori and make it the default encryptor.
+ scoped_ptr<Nigori> nigori(UnpackBootstrapToken(restored_bootstrap_token));
+ if (!nigori.get())
+ return false;
+ return AddKeyImpl(nigori.release());
+}
+
bool Cryptographer::AddKeyImpl(Nigori* initialized_nigori) {
scoped_ptr<Nigori> nigori(initialized_nigori);
std::string name;
diff --git a/chrome/browser/sync/util/cryptographer.h b/chrome/browser/sync/util/cryptographer.h
index 2c319ab..d5e98f7 100644
--- a/chrome/browser/sync/util/cryptographer.h
+++ b/chrome/browser/sync/util/cryptographer.h
@@ -131,6 +131,11 @@ class Cryptographer {
// Encrypt.
bool AddKey(const KeyParams& params);
+ // Same as AddKey(..), but builds the new Nigori from a previously persisted
+ // bootstrap token. This can be useful when consuming a bootstrap token
+ // with a cryptographer that has already been initialized.
+ bool AddKeyFromBootstrapToken(const std::string restored_bootstrap_token);
+
// Decrypts |encrypted| and uses its contents to initialize Nigori instances.
// Returns true unless decryption of |encrypted| fails. The caller is
// responsible for checking that CanDecrypt(encrypted) == true.