This source file includes following definitions.
- IsNigoriMigratedToKeystore
- ProtoPassphraseTypeToEnum
- EnumPassphraseTypeToProto
- IsExplicitPassphrase
- PackKeystoreBootstrapToken
- UnpackKeystoreBootstrapToken
- encrypted_types
- weak_ptr_factory_
- AddObserver
- RemoveObserver
- Init
- SetEncryptionPassphrase
- SetDecryptionPassphrase
- EnableEncryptEverything
- EncryptEverythingEnabled
- GetPassphraseType
- ApplyNigoriUpdate
- UpdateNigoriFromEncryptedTypes
- NeedKeystoreKey
- SetKeystoreKeys
- GetEncryptedTypes
- GetCryptographerUnsafe
- GetEncryptedTypesUnsafe
- MigratedToKeystore
- migration_time
- custom_passphrase_time
- ReEncryptEverything
- ApplyNigoriUpdateImpl
- RewriteNigori
- WriteEncryptionStateToNigori
- UpdateEncryptedTypesFromNigori
- SetCustomPassphrase
- DecryptPendingKeysWithExplicitPassphrase
- FinishSetPassphrase
- MergeEncryptedTypes
- UnlockVaultMutable
- UnlockVault
- ShouldTriggerMigration
- AttemptToMigrateNigoriToKeystore
- GetKeystoreDecryptor
- AttemptToInstallKeybag
- EnableEncryptEverythingImpl
- DecryptPendingKeysWithKeystoreKey
- GetExplicitPassphraseTime
#include "sync/internal_api/sync_encryption_handler_impl.h"
#include <queue>
#include <string>
#include "base/base64.h"
#include "base/bind.h"
#include "base/json/json_string_value_serializer.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/time/time.h"
#include "base/tracked_objects.h"
#include "sync/internal_api/public/read_node.h"
#include "sync/internal_api/public/read_transaction.h"
#include "sync/internal_api/public/user_share.h"
#include "sync/internal_api/public/util/experiments.h"
#include "sync/internal_api/public/util/sync_string_conversions.h"
#include "sync/internal_api/public/write_node.h"
#include "sync/internal_api/public/write_transaction.h"
#include "sync/protocol/encryption.pb.h"
#include "sync/protocol/nigori_specifics.pb.h"
#include "sync/protocol/sync.pb.h"
#include "sync/syncable/directory.h"
#include "sync/syncable/entry.h"
#include "sync/syncable/nigori_util.h"
#include "sync/syncable/syncable_base_transaction.h"
#include "sync/util/cryptographer.h"
#include "sync/util/encryptor.h"
#include "sync/util/time.h"
namespace syncer {
namespace {
static const int kNigoriOverwriteLimit = 10;
enum NigoriMigrationResult {
FAILED_TO_SET_DEFAULT_KEYSTORE,
FAILED_TO_SET_NONDEFAULT_KEYSTORE,
FAILED_TO_EXTRACT_DECRYPTOR,
FAILED_TO_EXTRACT_KEYBAG,
MIGRATION_SUCCESS_KEYSTORE_NONDEFAULT,
MIGRATION_SUCCESS_KEYSTORE_DEFAULT,
MIGRATION_SUCCESS_FROZEN_IMPLICIT,
MIGRATION_SUCCESS_CUSTOM,
MIGRATION_RESULT_SIZE,
};
enum NigoriMigrationState {
MIGRATED,
NOT_MIGRATED_CRYPTO_NOT_READY,
NOT_MIGRATED_NO_KEYSTORE_KEY,
NOT_MIGRATED_UNKNOWN_REASON,
MIGRATION_STATE_SIZE,
};
bool IsNigoriMigratedToKeystore(const sync_pb::NigoriSpecifics& nigori) {
if (!nigori.has_passphrase_type())
return false;
if (!nigori.has_keystore_migration_time())
return false;
if (!nigori.keybag_is_frozen())
return false;
if (nigori.passphrase_type() ==
sync_pb::NigoriSpecifics::IMPLICIT_PASSPHRASE)
return false;
if (nigori.passphrase_type() ==
sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE &&
nigori.keystore_decryptor_token().blob().empty())
return false;
if (!nigori.has_keystore_migration_time())
return false;
return true;
}
PassphraseType ProtoPassphraseTypeToEnum(
sync_pb::NigoriSpecifics::PassphraseType type) {
switch(type) {
case sync_pb::NigoriSpecifics::IMPLICIT_PASSPHRASE:
return IMPLICIT_PASSPHRASE;
case sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE:
return KEYSTORE_PASSPHRASE;
case sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE:
return CUSTOM_PASSPHRASE;
case sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE:
return FROZEN_IMPLICIT_PASSPHRASE;
default:
NOTREACHED();
return IMPLICIT_PASSPHRASE;
};
}
sync_pb::NigoriSpecifics::PassphraseType
EnumPassphraseTypeToProto(PassphraseType type) {
switch(type) {
case IMPLICIT_PASSPHRASE:
return sync_pb::NigoriSpecifics::IMPLICIT_PASSPHRASE;
case KEYSTORE_PASSPHRASE:
return sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE;
case CUSTOM_PASSPHRASE:
return sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE;
case FROZEN_IMPLICIT_PASSPHRASE:
return sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE;
default:
NOTREACHED();
return sync_pb::NigoriSpecifics::IMPLICIT_PASSPHRASE;
};
}
bool IsExplicitPassphrase(PassphraseType type) {
return type == CUSTOM_PASSPHRASE || type == FROZEN_IMPLICIT_PASSPHRASE;
}
std::string PackKeystoreBootstrapToken(
const std::vector<std::string>& old_keystore_keys,
const std::string& current_keystore_key,
Encryptor* encryptor) {
if (current_keystore_key.empty())
return std::string();
base::ListValue keystore_key_values;
for (size_t i = 0; i < old_keystore_keys.size(); ++i)
keystore_key_values.AppendString(old_keystore_keys[i]);
keystore_key_values.AppendString(current_keystore_key);
std::string serialized_keystores;
JSONStringValueSerializer json(&serialized_keystores);
json.Serialize(keystore_key_values);
std::string encrypted_keystores;
encryptor->EncryptString(serialized_keystores,
&encrypted_keystores);
std::string keystore_bootstrap;
base::Base64Encode(encrypted_keystores, &keystore_bootstrap);
return keystore_bootstrap;
}
bool UnpackKeystoreBootstrapToken(
const std::string& keystore_bootstrap_token,
Encryptor* encryptor,
std::vector<std::string>* old_keystore_keys,
std::string* current_keystore_key) {
if (keystore_bootstrap_token.empty())
return false;
std::string base64_decoded_keystore_bootstrap;
if (!base::Base64Decode(keystore_bootstrap_token,
&base64_decoded_keystore_bootstrap)) {
return false;
}
std::string decrypted_keystore_bootstrap;
if (!encryptor->DecryptString(base64_decoded_keystore_bootstrap,
&decrypted_keystore_bootstrap)) {
return false;
}
JSONStringValueSerializer json(&decrypted_keystore_bootstrap);
scoped_ptr<base::Value> deserialized_keystore_keys(
json.Deserialize(NULL, NULL));
if (!deserialized_keystore_keys)
return false;
base::ListValue* internal_list_value = NULL;
if (!deserialized_keystore_keys->GetAsList(&internal_list_value))
return false;
int number_of_keystore_keys = internal_list_value->GetSize();
if (!internal_list_value->GetString(number_of_keystore_keys - 1,
current_keystore_key)) {
return false;
}
old_keystore_keys->resize(number_of_keystore_keys - 1);
for (int i = 0; i < number_of_keystore_keys - 1; ++i)
internal_list_value->GetString(i, &(*old_keystore_keys)[i]);
return true;
}
}
SyncEncryptionHandlerImpl::Vault::Vault(
Encryptor* encryptor,
ModelTypeSet encrypted_types)
: cryptographer(encryptor),
encrypted_types(encrypted_types) {
}
SyncEncryptionHandlerImpl::Vault::~Vault() {
}
SyncEncryptionHandlerImpl::SyncEncryptionHandlerImpl(
UserShare* user_share,
Encryptor* encryptor,
const std::string& restored_key_for_bootstrapping,
const std::string& restored_keystore_key_for_bootstrapping)
: user_share_(user_share),
vault_unsafe_(encryptor, SensitiveTypes()),
encrypt_everything_(false),
passphrase_type_(IMPLICIT_PASSPHRASE),
nigori_overwrite_count_(0),
weak_ptr_factory_(this) {
vault_unsafe_.cryptographer.Bootstrap(restored_key_for_bootstrapping);
UnpackKeystoreBootstrapToken(
restored_keystore_key_for_bootstrapping,
encryptor,
&old_keystore_keys_,
&keystore_key_);
}
SyncEncryptionHandlerImpl::~SyncEncryptionHandlerImpl() {}
void SyncEncryptionHandlerImpl::AddObserver(Observer* observer) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!observers_.HasObserver(observer));
observers_.AddObserver(observer);
}
void SyncEncryptionHandlerImpl::RemoveObserver(Observer* observer) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(observers_.HasObserver(observer));
observers_.RemoveObserver(observer);
}
void SyncEncryptionHandlerImpl::Init() {
DCHECK(thread_checker_.CalledOnValidThread());
WriteTransaction trans(FROM_HERE, user_share_);
WriteNode node(&trans);
if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK)
return;
if (!ApplyNigoriUpdateImpl(node.GetNigoriSpecifics(),
trans.GetWrappedTrans())) {
WriteEncryptionStateToNigori(&trans);
}
bool has_pending_keys = UnlockVault(
trans.GetWrappedTrans()).cryptographer.has_pending_keys();
bool is_ready = UnlockVault(
trans.GetWrappedTrans()).cryptographer.is_ready();
UMA_HISTOGRAM_BOOLEAN("Sync.CryptographerReady", is_ready);
UMA_HISTOGRAM_BOOLEAN("Sync.CryptographerPendingKeys", has_pending_keys);
if (IsNigoriMigratedToKeystore(node.GetNigoriSpecifics())) {
UMA_HISTOGRAM_ENUMERATION("Sync.NigoriMigrationState",
MIGRATED,
MIGRATION_STATE_SIZE);
if (has_pending_keys && passphrase_type_ == KEYSTORE_PASSPHRASE) {
UMA_HISTOGRAM_BOOLEAN("Sync.KeystoreDecryptionFailed",
!keystore_key_.empty());
}
} else if (!is_ready) {
UMA_HISTOGRAM_ENUMERATION("Sync.NigoriMigrationState",
NOT_MIGRATED_CRYPTO_NOT_READY,
MIGRATION_STATE_SIZE);
} else if (keystore_key_.empty()) {
UMA_HISTOGRAM_ENUMERATION("Sync.NigoriMigrationState",
NOT_MIGRATED_NO_KEYSTORE_KEY,
MIGRATION_STATE_SIZE);
} else {
UMA_HISTOGRAM_ENUMERATION("Sync.NigoriMigrationState",
NOT_MIGRATED_UNKNOWN_REASON,
MIGRATION_STATE_SIZE);
}
FOR_EACH_OBSERVER(
Observer, observers_,
OnEncryptedTypesChanged(
UnlockVault(trans.GetWrappedTrans()).encrypted_types,
encrypt_everything_));
FOR_EACH_OBSERVER(
SyncEncryptionHandler::Observer,
observers_,
OnCryptographerStateChanged(
&UnlockVaultMutable(trans.GetWrappedTrans())->cryptographer));
if (UnlockVault(trans.GetWrappedTrans()).cryptographer.is_ready())
ReEncryptEverything(&trans);
}
void SyncEncryptionHandlerImpl::SetEncryptionPassphrase(
const std::string& passphrase,
bool is_explicit) {
DCHECK(thread_checker_.CalledOnValidThread());
if (passphrase.empty()) {
NOTREACHED() << "Cannot encrypt with an empty passphrase.";
return;
}
WriteTransaction trans(FROM_HERE, user_share_);
KeyParams key_params = {"localhost", "dummy", passphrase};
WriteNode node(&trans);
if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
NOTREACHED();
return;
}
Cryptographer* cryptographer =
&UnlockVaultMutable(trans.GetWrappedTrans())->cryptographer;
if (IsNigoriMigratedToKeystore(node.GetNigoriSpecifics())) {
if (!is_explicit) {
DCHECK(cryptographer->is_ready());
LOG(WARNING) << "Ignoring new implicit passphrase. Keystore migration "
<< "already performed.";
return;
}
SetCustomPassphrase(passphrase, &trans, &node);
UMA_HISTOGRAM_BOOLEAN("Sync.CustomEncryption", true);
return;
}
std::string bootstrap_token;
sync_pb::EncryptedData pending_keys;
if (cryptographer->has_pending_keys())
pending_keys = cryptographer->GetPendingKeys();
bool success = false;
if (!IsExplicitPassphrase(passphrase_type_)) {
if (!cryptographer->has_pending_keys()) {
if (cryptographer->AddKey(key_params)) {
if (is_explicit) {
DVLOG(1) << "Setting explicit passphrase for encryption.";
passphrase_type_ = CUSTOM_PASSPHRASE;
custom_passphrase_time_ = base::Time::Now();
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseTypeChanged(
passphrase_type_,
GetExplicitPassphraseTime()));
} else {
DVLOG(1) << "Setting implicit passphrase for encryption.";
}
cryptographer->GetBootstrapToken(&bootstrap_token);
UMA_HISTOGRAM_BOOLEAN("Sync.CustomEncryption", is_explicit);
success = true;
} else {
NOTREACHED() << "Failed to add key to cryptographer.";
success = false;
}
} else {
if (is_explicit) {
DVLOG(1) << "Failing because an implicit passphrase is already set.";
success = false;
} else {
if (cryptographer->DecryptPendingKeys(key_params)) {
DVLOG(1) << "Implicit internal passphrase accepted for decryption.";
cryptographer->GetBootstrapToken(&bootstrap_token);
success = true;
} else {
DVLOG(1) << "Implicit internal passphrase failed to decrypt, adding "
<< "anyways as default passphrase and persisting via "
<< "bootstrap token.";
Cryptographer temp_cryptographer(cryptographer->encryptor());
temp_cryptographer.AddKey(key_params);
temp_cryptographer.GetBootstrapToken(&bootstrap_token);
cryptographer->AddKey(key_params);
success = false;
}
}
}
} else {
DVLOG(1) << "Failing because an explicit passphrase is already set.";
success = false;
}
DVLOG_IF(1, !success)
<< "Failure in SetEncryptionPassphrase; notifying and returning.";
DVLOG_IF(1, success)
<< "Successfully set encryption passphrase; updating nigori and "
"reencrypting.";
FinishSetPassphrase(success, bootstrap_token, &trans, &node);
}
void SyncEncryptionHandlerImpl::SetDecryptionPassphrase(
const std::string& passphrase) {
DCHECK(thread_checker_.CalledOnValidThread());
if (passphrase.empty()) {
NOTREACHED() << "Cannot decrypt with an empty passphrase.";
return;
}
WriteTransaction trans(FROM_HERE, user_share_);
KeyParams key_params = {"localhost", "dummy", passphrase};
WriteNode node(&trans);
if (node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK) {
NOTREACHED();
return;
}
if (IsNigoriMigratedToKeystore(node.GetNigoriSpecifics()) &&
IsExplicitPassphrase(passphrase_type_)) {
DecryptPendingKeysWithExplicitPassphrase(passphrase, &trans, &node);
return;
}
Cryptographer* cryptographer =
&UnlockVaultMutable(trans.GetWrappedTrans())->cryptographer;
if (!cryptographer->has_pending_keys()) {
NOTREACHED() << "Attempt to set decryption passphrase failed because there "
<< "were no pending keys.";
return;
}
std::string bootstrap_token;
sync_pb::EncryptedData pending_keys;
pending_keys = cryptographer->GetPendingKeys();
bool success = false;
if (!IsExplicitPassphrase(passphrase_type_)) {
if (cryptographer->is_initialized()) {
Cryptographer temp_cryptographer(cryptographer->encryptor());
temp_cryptographer.SetPendingKeys(cryptographer->GetPendingKeys());
if (temp_cryptographer.DecryptPendingKeys(key_params)) {
sync_pb::EncryptedData encrypted;
cryptographer->GetKeys(&encrypted);
if (temp_cryptographer.CanDecrypt(encrypted)) {
DVLOG(1) << "Implicit user provided passphrase accepted for "
<< "decryption, overwriting default.";
cryptographer->DecryptPendingKeys(key_params);
cryptographer->GetBootstrapToken(&bootstrap_token);
success = true;
} else {
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);
cryptographer->AddKeyFromBootstrapToken(
bootstrap_token_from_current_key);
success = true;
}
} else {
DVLOG(1) << "Implicit user provided passphrase failed to decrypt.";
success = false;
}
} else {
if (cryptographer->DecryptPendingKeys(key_params)) {
cryptographer->GetBootstrapToken(&bootstrap_token);
DVLOG(1) << "Implicit user provided passphrase accepted, initializing"
<< " cryptographer.";
success = true;
} else {
DVLOG(1) << "Implicit user provided passphrase failed to decrypt.";
success = false;
}
}
} else {
if (cryptographer->DecryptPendingKeys(key_params)) {
DVLOG(1) << "Explicit passphrase accepted for decryption.";
cryptographer->GetBootstrapToken(&bootstrap_token);
success = true;
} else {
DVLOG(1) << "Explicit passphrase failed to decrypt.";
success = false;
}
}
DVLOG_IF(1, !success)
<< "Failure in SetDecryptionPassphrase; notifying and returning.";
DVLOG_IF(1, success)
<< "Successfully set decryption passphrase; updating nigori and "
"reencrypting.";
FinishSetPassphrase(success, bootstrap_token, &trans, &node);
}
void SyncEncryptionHandlerImpl::EnableEncryptEverything() {
DCHECK(thread_checker_.CalledOnValidThread());
WriteTransaction trans(FROM_HERE, user_share_);
DVLOG(1) << "Enabling encrypt everything.";
if (encrypt_everything_)
return;
EnableEncryptEverythingImpl(trans.GetWrappedTrans());
WriteEncryptionStateToNigori(&trans);
if (UnlockVault(trans.GetWrappedTrans()).cryptographer.is_ready())
ReEncryptEverything(&trans);
}
bool SyncEncryptionHandlerImpl::EncryptEverythingEnabled() const {
DCHECK(thread_checker_.CalledOnValidThread());
return encrypt_everything_;
}
PassphraseType SyncEncryptionHandlerImpl::GetPassphraseType() const {
DCHECK(thread_checker_.CalledOnValidThread());
return passphrase_type_;
}
void SyncEncryptionHandlerImpl::ApplyNigoriUpdate(
const sync_pb::NigoriSpecifics& nigori,
syncable::BaseTransaction* const trans) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(trans);
if (!ApplyNigoriUpdateImpl(nigori, trans)) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SyncEncryptionHandlerImpl::RewriteNigori,
weak_ptr_factory_.GetWeakPtr()));
}
FOR_EACH_OBSERVER(
SyncEncryptionHandler::Observer,
observers_,
OnCryptographerStateChanged(
&UnlockVaultMutable(trans)->cryptographer));
}
void SyncEncryptionHandlerImpl::UpdateNigoriFromEncryptedTypes(
sync_pb::NigoriSpecifics* nigori,
syncable::BaseTransaction* const trans) const {
DCHECK(thread_checker_.CalledOnValidThread());
syncable::UpdateNigoriFromEncryptedTypes(UnlockVault(trans).encrypted_types,
encrypt_everything_,
nigori);
}
bool SyncEncryptionHandlerImpl::NeedKeystoreKey(
syncable::BaseTransaction* const trans) const {
DCHECK(thread_checker_.CalledOnValidThread());
return keystore_key_.empty();
}
bool SyncEncryptionHandlerImpl::SetKeystoreKeys(
const google::protobuf::RepeatedPtrField<google::protobuf::string>& keys,
syncable::BaseTransaction* const trans) {
DCHECK(thread_checker_.CalledOnValidThread());
if (keys.size() == 0)
return false;
const std::string& raw_keystore_key = keys.Get(keys.size() - 1);
if (raw_keystore_key.empty())
return false;
base::Base64Encode(raw_keystore_key, &keystore_key_);
old_keystore_keys_.resize(keys.size() - 1);
for (int i = 0; i < keys.size() - 1; ++i)
base::Base64Encode(keys.Get(i), &old_keystore_keys_[i]);
Cryptographer* cryptographer = &UnlockVaultMutable(trans)->cryptographer;
std::string keystore_bootstrap = PackKeystoreBootstrapToken(
old_keystore_keys_,
keystore_key_,
cryptographer->encryptor());
DCHECK_EQ(keystore_bootstrap.empty(), keystore_key_.empty());
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnBootstrapTokenUpdated(keystore_bootstrap,
KEYSTORE_BOOTSTRAP_TOKEN));
DVLOG(1) << "Keystore bootstrap token updated.";
syncable::Entry entry(trans, syncable::GET_BY_SERVER_TAG, kNigoriTag);
if (!entry.good())
return true;
const sync_pb::NigoriSpecifics& nigori =
entry.GetSpecifics().nigori();
if (cryptographer->has_pending_keys() &&
IsNigoriMigratedToKeystore(nigori) &&
!nigori.keystore_decryptor_token().blob().empty()) {
DecryptPendingKeysWithKeystoreKey(keystore_key_,
nigori.keystore_decryptor_token(),
cryptographer);
}
if (ShouldTriggerMigration(nigori, *cryptographer)) {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&SyncEncryptionHandlerImpl::RewriteNigori,
weak_ptr_factory_.GetWeakPtr()));
}
return true;
}
ModelTypeSet SyncEncryptionHandlerImpl::GetEncryptedTypes(
syncable::BaseTransaction* const trans) const {
return UnlockVault(trans).encrypted_types;
}
Cryptographer* SyncEncryptionHandlerImpl::GetCryptographerUnsafe() {
DCHECK(thread_checker_.CalledOnValidThread());
return &vault_unsafe_.cryptographer;
}
ModelTypeSet SyncEncryptionHandlerImpl::GetEncryptedTypesUnsafe() {
DCHECK(thread_checker_.CalledOnValidThread());
return vault_unsafe_.encrypted_types;
}
bool SyncEncryptionHandlerImpl::MigratedToKeystore() {
DCHECK(thread_checker_.CalledOnValidThread());
ReadTransaction trans(FROM_HERE, user_share_);
ReadNode nigori_node(&trans);
if (nigori_node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK)
return false;
return IsNigoriMigratedToKeystore(nigori_node.GetNigoriSpecifics());
}
base::Time SyncEncryptionHandlerImpl::migration_time() const {
return migration_time_;
}
base::Time SyncEncryptionHandlerImpl::custom_passphrase_time() const {
return custom_passphrase_time_;
}
void SyncEncryptionHandlerImpl::ReEncryptEverything(
WriteTransaction* trans) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(UnlockVault(trans->GetWrappedTrans()).cryptographer.is_ready());
for (ModelTypeSet::Iterator iter =
UnlockVault(trans->GetWrappedTrans()).encrypted_types.First();
iter.Good(); iter.Inc()) {
if (iter.Get() == PASSWORDS || IsControlType(iter.Get()))
continue;
ReadNode type_root(trans);
std::string tag = ModelTypeToRootTag(iter.Get());
if (type_root.InitByTagLookup(tag) != BaseNode::INIT_OK)
continue;
std::queue<int64> to_visit;
int64 child_id = type_root.GetFirstChildId();
to_visit.push(child_id);
while (!to_visit.empty()) {
child_id = to_visit.front();
to_visit.pop();
if (child_id == kInvalidId)
continue;
WriteNode child(trans);
if (child.InitByIdLookup(child_id) != BaseNode::INIT_OK)
continue;
if (child.GetIsFolder()) {
to_visit.push(child.GetFirstChildId());
}
if (child.GetEntry()->GetUniqueServerTag().empty()) {
child.ResetFromSpecifics();
}
to_visit.push(child.GetSuccessorId());
}
}
ReadNode passwords_root(trans);
std::string passwords_tag = ModelTypeToRootTag(PASSWORDS);
if (passwords_root.InitByTagLookup(passwords_tag) ==
BaseNode::INIT_OK) {
int64 child_id = passwords_root.GetFirstChildId();
while (child_id != kInvalidId) {
WriteNode child(trans);
if (child.InitByIdLookup(child_id) != BaseNode::INIT_OK) {
NOTREACHED();
return;
}
child.SetPasswordSpecifics(child.GetPasswordSpecifics());
child_id = child.GetSuccessorId();
}
}
DVLOG(1) << "Re-encrypt everything complete.";
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnEncryptionComplete());
}
bool SyncEncryptionHandlerImpl::ApplyNigoriUpdateImpl(
const sync_pb::NigoriSpecifics& nigori,
syncable::BaseTransaction* const trans) {
DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "Applying nigori node update.";
bool nigori_types_need_update = !UpdateEncryptedTypesFromNigori(nigori,
trans);
if (nigori.custom_passphrase_time() != 0) {
custom_passphrase_time_ =
ProtoTimeToTime(nigori.custom_passphrase_time());
}
bool is_nigori_migrated = IsNigoriMigratedToKeystore(nigori);
if (is_nigori_migrated) {
DCHECK(nigori.has_keystore_migration_time());
migration_time_ = ProtoTimeToTime(nigori.keystore_migration_time());
PassphraseType nigori_passphrase_type =
ProtoPassphraseTypeToEnum(nigori.passphrase_type());
if (passphrase_type_ != nigori_passphrase_type &&
nigori_passphrase_type != IMPLICIT_PASSPHRASE &&
(passphrase_type_ == IMPLICIT_PASSPHRASE ||
nigori_passphrase_type == CUSTOM_PASSPHRASE)) {
DVLOG(1) << "Changing passphrase state from "
<< PassphraseTypeToString(passphrase_type_)
<< " to "
<< PassphraseTypeToString(nigori_passphrase_type);
passphrase_type_ = nigori_passphrase_type;
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseTypeChanged(
passphrase_type_,
GetExplicitPassphraseTime()));
}
if (passphrase_type_ == KEYSTORE_PASSPHRASE && encrypt_everything_) {
DVLOG(1) << "Changing passphrase state to FROZEN_IMPLICIT_PASSPHRASE "
<< "due to full encryption.";
passphrase_type_ = FROZEN_IMPLICIT_PASSPHRASE;
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseTypeChanged(
passphrase_type_,
GetExplicitPassphraseTime()));
}
} else {
if (nigori.keybag_is_frozen() &&
passphrase_type_ != CUSTOM_PASSPHRASE) {
passphrase_type_ = CUSTOM_PASSPHRASE;
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseTypeChanged(
passphrase_type_,
GetExplicitPassphraseTime()));
}
}
Cryptographer* cryptographer = &UnlockVaultMutable(trans)->cryptographer;
bool nigori_needs_new_keys = false;
if (!nigori.encryption_keybag().blob().empty()) {
bool need_new_default_key = false;
if (is_nigori_migrated) {
need_new_default_key = IsExplicitPassphrase(
ProtoPassphraseTypeToEnum(nigori.passphrase_type()));
} else {
need_new_default_key = nigori.keybag_is_frozen();
}
if (!AttemptToInstallKeybag(nigori.encryption_keybag(),
need_new_default_key,
cryptographer)) {
cryptographer->SetPendingKeys(nigori.encryption_keybag());
if (!nigori.keystore_decryptor_token().blob().empty() &&
!keystore_key_.empty()) {
if (DecryptPendingKeysWithKeystoreKey(keystore_key_,
nigori.keystore_decryptor_token(),
cryptographer)) {
nigori_needs_new_keys =
cryptographer->KeybagIsStale(nigori.encryption_keybag());
} else {
LOG(ERROR) << "Failed to decrypt pending keys using keystore "
<< "bootstrap key.";
}
}
} else {
nigori_needs_new_keys =
cryptographer->KeybagIsStale(nigori.encryption_keybag());
}
} else {
LOG(WARNING) << "Nigori had empty encryption keybag.";
nigori_needs_new_keys = true;
}
if (cryptographer->has_pending_keys()) {
DVLOG(1) << "OnPassphraseRequired Sent";
sync_pb::EncryptedData pending_keys = cryptographer->GetPendingKeys();
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseRequired(REASON_DECRYPTION,
pending_keys));
} else if (!cryptographer->is_ready()) {
DVLOG(1) << "OnPassphraseRequired sent because cryptographer is not "
<< "ready";
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseRequired(REASON_ENCRYPTION,
sync_pb::EncryptedData()));
}
bool passphrase_type_matches = true;
if (!is_nigori_migrated) {
DCHECK(passphrase_type_ == CUSTOM_PASSPHRASE ||
passphrase_type_ == IMPLICIT_PASSPHRASE);
passphrase_type_matches =
nigori.keybag_is_frozen() == IsExplicitPassphrase(passphrase_type_);
} else {
passphrase_type_matches =
(ProtoPassphraseTypeToEnum(nigori.passphrase_type()) ==
passphrase_type_);
}
if (!passphrase_type_matches ||
nigori.encrypt_everything() != encrypt_everything_ ||
nigori_types_need_update ||
nigori_needs_new_keys) {
DVLOG(1) << "Triggering nigori rewrite.";
return false;
}
return true;
}
void SyncEncryptionHandlerImpl::RewriteNigori() {
DVLOG(1) << "Writing local encryption state into nigori.";
DCHECK(thread_checker_.CalledOnValidThread());
WriteTransaction trans(FROM_HERE, user_share_);
WriteEncryptionStateToNigori(&trans);
}
void SyncEncryptionHandlerImpl::WriteEncryptionStateToNigori(
WriteTransaction* trans) {
DCHECK(thread_checker_.CalledOnValidThread());
WriteNode nigori_node(trans);
if (nigori_node.InitByTagLookup(kNigoriTag) != BaseNode::INIT_OK)
return;
sync_pb::NigoriSpecifics nigori = nigori_node.GetNigoriSpecifics();
const Cryptographer& cryptographer =
UnlockVault(trans->GetWrappedTrans()).cryptographer;
if (!AttemptToMigrateNigoriToKeystore(trans, &nigori_node)) {
if (cryptographer.is_ready() &&
nigori_overwrite_count_ < kNigoriOverwriteLimit) {
sync_pb::EncryptedData original_keys = nigori.encryption_keybag();
if (!cryptographer.GetKeys(nigori.mutable_encryption_keybag()))
NOTREACHED();
if (nigori.encryption_keybag().SerializeAsString() !=
original_keys.SerializeAsString()) {
nigori_overwrite_count_++;
UMA_HISTOGRAM_COUNTS("Sync.AutoNigoriOverwrites",
nigori_overwrite_count_);
}
}
syncable::UpdateNigoriFromEncryptedTypes(
UnlockVault(trans->GetWrappedTrans()).encrypted_types,
encrypt_everything_,
&nigori);
if (!custom_passphrase_time_.is_null()) {
nigori.set_custom_passphrase_time(
TimeToProtoTime(custom_passphrase_time_));
}
nigori_node.SetNigoriSpecifics(nigori);
}
}
bool SyncEncryptionHandlerImpl::UpdateEncryptedTypesFromNigori(
const sync_pb::NigoriSpecifics& nigori,
syncable::BaseTransaction* const trans) {
DCHECK(thread_checker_.CalledOnValidThread());
ModelTypeSet* encrypted_types = &UnlockVaultMutable(trans)->encrypted_types;
if (nigori.encrypt_everything()) {
EnableEncryptEverythingImpl(trans);
DCHECK(encrypted_types->Equals(EncryptableUserTypes()));
return true;
} else if (encrypt_everything_) {
DCHECK(encrypted_types->Equals(EncryptableUserTypes()));
return false;
}
ModelTypeSet nigori_encrypted_types;
nigori_encrypted_types = syncable::GetEncryptedTypesFromNigori(nigori);
nigori_encrypted_types.PutAll(SensitiveTypes());
if (!nigori.has_encrypt_everything() &&
!Difference(nigori_encrypted_types, SensitiveTypes()).Empty()) {
if (!encrypt_everything_) {
encrypt_everything_ = true;
*encrypted_types = EncryptableUserTypes();
FOR_EACH_OBSERVER(
Observer, observers_,
OnEncryptedTypesChanged(*encrypted_types, encrypt_everything_));
}
DCHECK(encrypted_types->Equals(EncryptableUserTypes()));
return false;
}
MergeEncryptedTypes(nigori_encrypted_types, trans);
return encrypted_types->Equals(nigori_encrypted_types);
}
void SyncEncryptionHandlerImpl::SetCustomPassphrase(
const std::string& passphrase,
WriteTransaction* trans,
WriteNode* nigori_node) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(IsNigoriMigratedToKeystore(nigori_node->GetNigoriSpecifics()));
KeyParams key_params = {"localhost", "dummy", passphrase};
if (passphrase_type_ != KEYSTORE_PASSPHRASE) {
DVLOG(1) << "Failing to set a custom passphrase because one has already "
<< "been set.";
FinishSetPassphrase(false, std::string(), trans, nigori_node);
return;
}
Cryptographer* cryptographer =
&UnlockVaultMutable(trans->GetWrappedTrans())->cryptographer;
if (cryptographer->has_pending_keys()) {
LOG(ERROR) << "Failing to set custom passphrase because of pending keys.";
FinishSetPassphrase(false, std::string(), trans, nigori_node);
return;
}
std::string bootstrap_token;
if (cryptographer->AddKey(key_params)) {
DVLOG(1) << "Setting custom passphrase.";
cryptographer->GetBootstrapToken(&bootstrap_token);
passphrase_type_ = CUSTOM_PASSPHRASE;
custom_passphrase_time_ = base::Time::Now();
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseTypeChanged(
passphrase_type_,
GetExplicitPassphraseTime()));
} else {
NOTREACHED() << "Failed to add key to cryptographer.";
return;
}
FinishSetPassphrase(true, bootstrap_token, trans, nigori_node);
}
void SyncEncryptionHandlerImpl::DecryptPendingKeysWithExplicitPassphrase(
const std::string& passphrase,
WriteTransaction* trans,
WriteNode* nigori_node) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(IsExplicitPassphrase(passphrase_type_));
KeyParams key_params = {"localhost", "dummy", passphrase};
Cryptographer* cryptographer =
&UnlockVaultMutable(trans->GetWrappedTrans())->cryptographer;
if (!cryptographer->has_pending_keys()) {
NOTREACHED() << "Attempt to set decryption passphrase failed because there "
<< "were no pending keys.";
return;
}
DCHECK(IsExplicitPassphrase(passphrase_type_));
bool success = false;
std::string bootstrap_token;
if (cryptographer->DecryptPendingKeys(key_params)) {
DVLOG(1) << "Explicit passphrase accepted for decryption.";
cryptographer->GetBootstrapToken(&bootstrap_token);
success = true;
} else {
DVLOG(1) << "Explicit passphrase failed to decrypt.";
success = false;
}
if (success && !keystore_key_.empty()) {
KeyParams key_params = {"localhost", "dummy", keystore_key_};
cryptographer->AddNonDefaultKey(key_params);
}
FinishSetPassphrase(success, bootstrap_token, trans, nigori_node);
}
void SyncEncryptionHandlerImpl::FinishSetPassphrase(
bool success,
const std::string& bootstrap_token,
WriteTransaction* trans,
WriteNode* nigori_node) {
DCHECK(thread_checker_.CalledOnValidThread());
FOR_EACH_OBSERVER(
SyncEncryptionHandler::Observer,
observers_,
OnCryptographerStateChanged(
&UnlockVaultMutable(trans->GetWrappedTrans())->cryptographer));
if (!bootstrap_token.empty()) {
DVLOG(1) << "Passphrase bootstrap token updated.";
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnBootstrapTokenUpdated(bootstrap_token,
PASSPHRASE_BOOTSTRAP_TOKEN));
}
const Cryptographer& cryptographer =
UnlockVault(trans->GetWrappedTrans()).cryptographer;
if (!success) {
if (cryptographer.is_ready()) {
LOG(ERROR) << "Attempt to change passphrase failed while cryptographer "
<< "was ready.";
} else if (cryptographer.has_pending_keys()) {
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseRequired(REASON_DECRYPTION,
cryptographer.GetPendingKeys()));
} else {
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseRequired(REASON_ENCRYPTION,
sync_pb::EncryptedData()));
}
return;
}
DCHECK(success);
DCHECK(cryptographer.is_ready());
if (!AttemptToMigrateNigoriToKeystore(trans, nigori_node)) {
sync_pb::NigoriSpecifics nigori(nigori_node->GetNigoriSpecifics());
if (!cryptographer.GetKeys(nigori.mutable_encryption_keybag()))
NOTREACHED();
if (IsNigoriMigratedToKeystore(nigori)) {
DCHECK(keystore_key_.empty() || IsExplicitPassphrase(passphrase_type_));
DVLOG(1) << "Leaving nigori migration state untouched after setting"
<< " passphrase.";
} else {
nigori.set_keybag_is_frozen(
IsExplicitPassphrase(passphrase_type_));
}
if (!custom_passphrase_time_.is_null()) {
nigori.set_custom_passphrase_time(
TimeToProtoTime(custom_passphrase_time_));
}
nigori_node->SetNigoriSpecifics(nigori);
}
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseAccepted());
ReEncryptEverything(trans);
}
void SyncEncryptionHandlerImpl::MergeEncryptedTypes(
ModelTypeSet new_encrypted_types,
syncable::BaseTransaction* const trans) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(EncryptableUserTypes().HasAll(new_encrypted_types));
ModelTypeSet* encrypted_types = &UnlockVaultMutable(trans)->encrypted_types;
if (!encrypted_types->HasAll(new_encrypted_types)) {
*encrypted_types = new_encrypted_types;
FOR_EACH_OBSERVER(
Observer, observers_,
OnEncryptedTypesChanged(*encrypted_types, encrypt_everything_));
}
}
SyncEncryptionHandlerImpl::Vault* SyncEncryptionHandlerImpl::UnlockVaultMutable(
syncable::BaseTransaction* const trans) {
DCHECK_EQ(user_share_->directory.get(), trans->directory());
return &vault_unsafe_;
}
const SyncEncryptionHandlerImpl::Vault& SyncEncryptionHandlerImpl::UnlockVault(
syncable::BaseTransaction* const trans) const {
DCHECK_EQ(user_share_->directory.get(), trans->directory());
return vault_unsafe_;
}
bool SyncEncryptionHandlerImpl::ShouldTriggerMigration(
const sync_pb::NigoriSpecifics& nigori,
const Cryptographer& cryptographer) const {
DCHECK(thread_checker_.CalledOnValidThread());
if (cryptographer.has_pending_keys())
return false;
if (IsNigoriMigratedToKeystore(nigori)) {
if (passphrase_type_ != KEYSTORE_PASSPHRASE &&
nigori.passphrase_type() ==
sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE) {
return true;
} else if (IsExplicitPassphrase(passphrase_type_) &&
!encrypt_everything_) {
return true;
} else if (passphrase_type_ == KEYSTORE_PASSPHRASE &&
encrypt_everything_) {
return true;
} else if (
cryptographer.is_ready() &&
!cryptographer.CanDecryptUsingDefaultKey(nigori.encryption_keybag())) {
return true;
} else if (old_keystore_keys_.size() > 0 && !keystore_key_.empty()) {
Cryptographer temp_cryptographer(cryptographer.encryptor());
KeyParams keystore_params = {"localhost", "dummy", keystore_key_};
temp_cryptographer.AddKey(keystore_params);
if (!temp_cryptographer.CanDecryptUsingDefaultKey(
nigori.encryption_keybag())) {
return true;
}
}
return false;
} else if (keystore_key_.empty()) {
return false;
}
return true;
}
bool SyncEncryptionHandlerImpl::AttemptToMigrateNigoriToKeystore(
WriteTransaction* trans,
WriteNode* nigori_node) {
DCHECK(thread_checker_.CalledOnValidThread());
const sync_pb::NigoriSpecifics& old_nigori =
nigori_node->GetNigoriSpecifics();
Cryptographer* cryptographer =
&UnlockVaultMutable(trans->GetWrappedTrans())->cryptographer;
if (!ShouldTriggerMigration(old_nigori, *cryptographer))
return false;
DVLOG(1) << "Starting nigori migration to keystore support.";
sync_pb::NigoriSpecifics migrated_nigori(old_nigori);
PassphraseType new_passphrase_type = passphrase_type_;
bool new_encrypt_everything = encrypt_everything_;
if (encrypt_everything_ && !IsExplicitPassphrase(passphrase_type_)) {
DVLOG(1) << "Switching to frozen implicit passphrase due to already having "
<< "full encryption.";
new_passphrase_type = FROZEN_IMPLICIT_PASSPHRASE;
migrated_nigori.clear_keystore_decryptor_token();
} else if (IsExplicitPassphrase(passphrase_type_)) {
DVLOG_IF(1, !encrypt_everything_) << "Enabling encrypt everything due to "
<< "explicit passphrase";
new_encrypt_everything = true;
migrated_nigori.clear_keystore_decryptor_token();
} else {
DCHECK(!encrypt_everything_);
new_passphrase_type = KEYSTORE_PASSPHRASE;
DVLOG(1) << "Switching to keystore passphrase state.";
}
migrated_nigori.set_encrypt_everything(new_encrypt_everything);
migrated_nigori.set_passphrase_type(
EnumPassphraseTypeToProto(new_passphrase_type));
migrated_nigori.set_keybag_is_frozen(true);
if (!keystore_key_.empty()) {
KeyParams key_params = {"localhost", "dummy", keystore_key_};
if ((old_keystore_keys_.size() > 0 &&
new_passphrase_type == KEYSTORE_PASSPHRASE) ||
!cryptographer->is_initialized()) {
DVLOG(1) << "Migrating keybag to keystore key.";
bool cryptographer_was_ready = cryptographer->is_ready();
if (!cryptographer->AddKey(key_params)) {
LOG(ERROR) << "Failed to add keystore key as default key";
UMA_HISTOGRAM_ENUMERATION("Sync.AttemptNigoriMigration",
FAILED_TO_SET_DEFAULT_KEYSTORE,
MIGRATION_RESULT_SIZE);
return false;
}
if (!cryptographer_was_ready && cryptographer->is_ready()) {
FOR_EACH_OBSERVER(
SyncEncryptionHandler::Observer,
observers_,
OnPassphraseAccepted());
}
} else {
DVLOG(1) << "Migrating keybag while preserving old key";
if (!cryptographer->AddNonDefaultKey(key_params)) {
LOG(ERROR) << "Failed to add keystore key as non-default key.";
UMA_HISTOGRAM_ENUMERATION("Sync.AttemptNigoriMigration",
FAILED_TO_SET_NONDEFAULT_KEYSTORE,
MIGRATION_RESULT_SIZE);
return false;
}
}
}
if (!old_keystore_keys_.empty()) {
for (std::vector<std::string>::const_iterator iter =
old_keystore_keys_.begin(); iter != old_keystore_keys_.end();
++iter) {
KeyParams key_params = {"localhost", "dummy", *iter};
cryptographer->AddNonDefaultKey(key_params);
}
}
if (new_passphrase_type == KEYSTORE_PASSPHRASE &&
!GetKeystoreDecryptor(
*cryptographer,
keystore_key_,
migrated_nigori.mutable_keystore_decryptor_token())) {
LOG(ERROR) << "Failed to extract keystore decryptor token.";
UMA_HISTOGRAM_ENUMERATION("Sync.AttemptNigoriMigration",
FAILED_TO_EXTRACT_DECRYPTOR,
MIGRATION_RESULT_SIZE);
return false;
}
if (!cryptographer->GetKeys(migrated_nigori.mutable_encryption_keybag())) {
LOG(ERROR) << "Failed to extract encryption keybag.";
UMA_HISTOGRAM_ENUMERATION("Sync.AttemptNigoriMigration",
FAILED_TO_EXTRACT_KEYBAG,
MIGRATION_RESULT_SIZE);
return false;
}
if (migration_time_.is_null())
migration_time_ = base::Time::Now();
migrated_nigori.set_keystore_migration_time(TimeToProtoTime(migration_time_));
if (!custom_passphrase_time_.is_null()) {
migrated_nigori.set_custom_passphrase_time(
TimeToProtoTime(custom_passphrase_time_));
}
FOR_EACH_OBSERVER(
SyncEncryptionHandler::Observer,
observers_,
OnCryptographerStateChanged(cryptographer));
if (passphrase_type_ != new_passphrase_type) {
passphrase_type_ = new_passphrase_type;
FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
OnPassphraseTypeChanged(
passphrase_type_,
GetExplicitPassphraseTime()));
}
if (new_encrypt_everything && !encrypt_everything_) {
EnableEncryptEverythingImpl(trans->GetWrappedTrans());
ReEncryptEverything(trans);
} else if (!cryptographer->CanDecryptUsingDefaultKey(
old_nigori.encryption_keybag())) {
DVLOG(1) << "Rencrypting everything due to key rotation.";
ReEncryptEverything(trans);
}
DVLOG(1) << "Completing nigori migration to keystore support.";
nigori_node->SetNigoriSpecifics(migrated_nigori);
switch (new_passphrase_type) {
case KEYSTORE_PASSPHRASE:
if (old_keystore_keys_.size() > 0) {
UMA_HISTOGRAM_ENUMERATION("Sync.AttemptNigoriMigration",
MIGRATION_SUCCESS_KEYSTORE_NONDEFAULT,
MIGRATION_RESULT_SIZE);
} else {
UMA_HISTOGRAM_ENUMERATION("Sync.AttemptNigoriMigration",
MIGRATION_SUCCESS_KEYSTORE_DEFAULT,
MIGRATION_RESULT_SIZE);
}
break;
case FROZEN_IMPLICIT_PASSPHRASE:
UMA_HISTOGRAM_ENUMERATION("Sync.AttemptNigoriMigration",
MIGRATION_SUCCESS_FROZEN_IMPLICIT,
MIGRATION_RESULT_SIZE);
break;
case CUSTOM_PASSPHRASE:
UMA_HISTOGRAM_ENUMERATION("Sync.AttemptNigoriMigration",
MIGRATION_SUCCESS_CUSTOM,
MIGRATION_RESULT_SIZE);
break;
default:
NOTREACHED();
break;
}
return true;
}
bool SyncEncryptionHandlerImpl::GetKeystoreDecryptor(
const Cryptographer& cryptographer,
const std::string& keystore_key,
sync_pb::EncryptedData* encrypted_blob) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!keystore_key.empty());
DCHECK(cryptographer.is_ready());
std::string serialized_nigori;
serialized_nigori = cryptographer.GetDefaultNigoriKey();
if (serialized_nigori.empty()) {
LOG(ERROR) << "Failed to get cryptographer bootstrap token.";
return false;
}
Cryptographer temp_cryptographer(cryptographer.encryptor());
KeyParams key_params = {"localhost", "dummy", keystore_key};
if (!temp_cryptographer.AddKey(key_params))
return false;
if (!temp_cryptographer.EncryptString(serialized_nigori, encrypted_blob))
return false;
return true;
}
bool SyncEncryptionHandlerImpl::AttemptToInstallKeybag(
const sync_pb::EncryptedData& keybag,
bool update_default,
Cryptographer* cryptographer) {
if (!cryptographer->CanDecrypt(keybag))
return false;
cryptographer->InstallKeys(keybag);
if (update_default)
cryptographer->SetDefaultKey(keybag.key_name());
return true;
}
void SyncEncryptionHandlerImpl::EnableEncryptEverythingImpl(
syncable::BaseTransaction* const trans) {
ModelTypeSet* encrypted_types = &UnlockVaultMutable(trans)->encrypted_types;
if (encrypt_everything_) {
DCHECK(encrypted_types->Equals(EncryptableUserTypes()));
return;
}
encrypt_everything_ = true;
*encrypted_types = EncryptableUserTypes();
FOR_EACH_OBSERVER(
Observer, observers_,
OnEncryptedTypesChanged(*encrypted_types, encrypt_everything_));
}
bool SyncEncryptionHandlerImpl::DecryptPendingKeysWithKeystoreKey(
const std::string& keystore_key,
const sync_pb::EncryptedData& keystore_decryptor_token,
Cryptographer* cryptographer) {
DCHECK(cryptographer->has_pending_keys());
if (keystore_decryptor_token.blob().empty())
return false;
Cryptographer temp_cryptographer(cryptographer->encryptor());
for (size_t i = 0; i < old_keystore_keys_.size(); ++i) {
KeyParams old_key_params = {"localhost", "dummy", old_keystore_keys_[i]};
temp_cryptographer.AddKey(old_key_params);
}
KeyParams keystore_params = {"localhost", "dummy", keystore_key_};
if (temp_cryptographer.AddKey(keystore_params) &&
temp_cryptographer.CanDecrypt(keystore_decryptor_token)) {
DVLOG(1) << "Attempting to decrypt pending keys using "
<< "keystore decryptor token.";
std::string serialized_nigori =
temp_cryptographer.DecryptToString(keystore_decryptor_token);
cryptographer->ImportNigoriKey(serialized_nigori);
if (!temp_cryptographer.CanDecryptUsingDefaultKey(
keystore_decryptor_token)) {
DVLOG(1) << "Pending keys based on old keystore key. Setting newest "
<< "keystore key as default.";
cryptographer->AddKey(keystore_params);
} else {
DVLOG(1) << "Pending keys based on newest keystore key.";
cryptographer->AddNonDefaultKey(keystore_params);
}
if (cryptographer->is_ready()) {
std::string bootstrap_token;
cryptographer->GetBootstrapToken(&bootstrap_token);
DVLOG(1) << "Keystore decryptor token decrypted pending keys.";
FOR_EACH_OBSERVER(
SyncEncryptionHandler::Observer,
observers_,
OnPassphraseAccepted());
FOR_EACH_OBSERVER(
SyncEncryptionHandler::Observer,
observers_,
OnBootstrapTokenUpdated(bootstrap_token,
PASSPHRASE_BOOTSTRAP_TOKEN));
FOR_EACH_OBSERVER(
SyncEncryptionHandler::Observer,
observers_,
OnCryptographerStateChanged(cryptographer));
return true;
}
}
return false;
}
base::Time SyncEncryptionHandlerImpl::GetExplicitPassphraseTime() const {
if (passphrase_type_ == FROZEN_IMPLICIT_PASSPHRASE)
return migration_time();
else if (passphrase_type_ == CUSTOM_PASSPHRASE)
return custom_passphrase_time();
return base::Time();
}
}