diff options
Diffstat (limited to 'sync/engine/model_type_sync_worker_impl.cc')
-rw-r--r-- | sync/engine/model_type_sync_worker_impl.cc | 209 |
1 files changed, 188 insertions, 21 deletions
diff --git a/sync/engine/model_type_sync_worker_impl.cc b/sync/engine/model_type_sync_worker_impl.cc index a5402c2..a58400f4 100644 --- a/sync/engine/model_type_sync_worker_impl.cc +++ b/sync/engine/model_type_sync_worker_impl.cc @@ -13,6 +13,7 @@ #include "sync/engine/model_type_sync_proxy.h" #include "sync/engine/non_blocking_type_commit_contribution.h" #include "sync/syncable/syncable_util.h" +#include "sync/util/cryptographer.h" #include "sync/util/time.h" namespace syncer { @@ -20,11 +21,14 @@ namespace syncer { ModelTypeSyncWorkerImpl::ModelTypeSyncWorkerImpl( ModelType type, const DataTypeState& initial_state, + const UpdateResponseDataList& saved_pending_updates, + CryptographerProvider* cryptographer_provider, NudgeHandler* nudge_handler, scoped_ptr<ModelTypeSyncProxy> type_sync_proxy) : type_(type), data_type_state_(initial_state), type_sync_proxy_(type_sync_proxy.Pass()), + cryptographer_provider_(cryptographer_provider), nudge_handler_(nudge_handler), entities_deleter_(&entities_), weak_ptr_factory_(this) { @@ -32,6 +36,18 @@ ModelTypeSyncWorkerImpl::ModelTypeSyncWorkerImpl( if (!data_type_state_.initial_sync_done) { nudge_handler_->NudgeForInitialDownload(type_); } + + for (UpdateResponseDataList::const_iterator it = + saved_pending_updates.begin(); + it != saved_pending_updates.end(); + ++it) { + EntityTracker* entity_tracker = EntityTracker::FromServerUpdate( + it->id, it->client_tag_hash, it->response_version); + entity_tracker->ReceivePendingUpdate(*it); + entities_.insert(std::make_pair(it->client_tag_hash, entity_tracker)); + } + + TryDecryptPendingUpdates(); } ModelTypeSyncWorkerImpl::~ModelTypeSyncWorkerImpl() { @@ -42,6 +58,33 @@ ModelType ModelTypeSyncWorkerImpl::GetModelType() const { return type_; } +bool ModelTypeSyncWorkerImpl::IsEncryptionRequired() const { + return !data_type_state_.encryption_key_name.empty(); +} + +void ModelTypeSyncWorkerImpl::SetEncryptionKeyName(const std::string& name) { + if (data_type_state_.encryption_key_name == name) + return; + + data_type_state_.encryption_key_name = name; + + // Pretend to send an update. This will cause the TypeSyncProxy to notice + // the new encryption key and take appropriate action. + type_sync_proxy_->OnUpdateReceived( + data_type_state_, UpdateResponseDataList(), UpdateResponseDataList()); +} + +void ModelTypeSyncWorkerImpl::OnCryptographerStateChanged() { + TryDecryptPendingUpdates(); + + ScopedCryptographerRef scoped_cryptographer_ref; + cryptographer_provider_->InitScopedCryptographerRef( + &scoped_cryptographer_ref); + Cryptographer* cryptographer = scoped_cryptographer_ref.Get(); + if (CanCommitItems(cryptographer)) + nudge_handler_->NudgeForCommit(type_); +} + // UpdateHandler implementation. void ModelTypeSyncWorkerImpl::GetDownloadProgress( sync_pb::DataTypeProgressMarker* progress_marker) const { @@ -66,7 +109,14 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse( data_type_state_.type_context = mutated_context; data_type_state_.progress_marker = progress_marker; + ScopedCryptographerRef scoped_cryptographer_ref; + cryptographer_provider_->InitScopedCryptographerRef( + &scoped_cryptographer_ref); + Cryptographer* cryptographer = scoped_cryptographer_ref.Get(); + DCHECK(cryptographer); + UpdateResponseDataList response_datas; + UpdateResponseDataList pending_updates; for (SyncEntityList::const_iterator update_it = applicable_updates.begin(); update_it != applicable_updates.end(); @@ -86,16 +136,17 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse( const std::string& client_tag_hash = update_entity->client_defined_unique_tag(); DCHECK(!client_tag_hash.empty()); + + EntityTracker* entity_tracker = NULL; EntityMap::const_iterator map_it = entities_.find(client_tag_hash); if (map_it == entities_.end()) { - EntityTracker* entity = + entity_tracker = EntityTracker::FromServerUpdate(update_entity->id_string(), client_tag_hash, update_entity->version()); - entities_.insert(std::make_pair(client_tag_hash, entity)); + entities_.insert(std::make_pair(client_tag_hash, entity_tracker)); } else { - EntityTracker* entity = map_it->second; - entity->ReceiveUpdate(update_entity->version()); + entity_tracker = map_it->second; } // Prepare the message for the model thread. @@ -107,14 +158,39 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse( response_data.mtime = ProtoTimeToTime(update_entity->mtime()); response_data.non_unique_name = update_entity->name(); response_data.deleted = update_entity->deleted(); - response_data.specifics = update_entity->specifics(); - response_datas.push_back(response_data); + const sync_pb::EntitySpecifics& specifics = update_entity->specifics(); + + if (!specifics.has_encrypted()) { + // No encryption. + entity_tracker->ReceiveUpdate(update_entity->version()); + response_data.specifics = specifics; + response_datas.push_back(response_data); + } else if (specifics.has_encrypted() && + cryptographer->CanDecrypt(specifics.encrypted())) { + // Encrypted, but we know the key. + if (DecryptSpecifics( + cryptographer, specifics, &response_data.specifics)) { + entity_tracker->ReceiveUpdate(update_entity->version()); + response_data.encryption_key_name = specifics.encrypted().key_name(); + response_datas.push_back(response_data); + } + } else if (specifics.has_encrypted() && + !cryptographer->CanDecrypt(specifics.encrypted())) { + // Can't decrypt right now. Ask the entity tracker to handle it. + response_data.specifics = specifics; + if (entity_tracker->ReceivePendingUpdate(response_data)) { + // Send to the model thread for safe-keeping across restarts if the + // tracker decides the update is worth keeping. + pending_updates.push_back(response_data); + } + } } } // Forward these updates to the model thread so it can do the rest. - type_sync_proxy_->OnUpdateReceived(data_type_state_, response_datas); + type_sync_proxy_->OnUpdateReceived( + data_type_state_, response_datas, pending_updates); return SYNCER_OK; } @@ -128,8 +204,8 @@ void ModelTypeSyncWorkerImpl::ApplyUpdates(sessions::StatusController* status) { if (!data_type_state_.initial_sync_done) { data_type_state_.initial_sync_done = true; - UpdateResponseDataList empty_update_list; - type_sync_proxy_->OnUpdateReceived(data_type_state_, empty_update_list); + type_sync_proxy_->OnUpdateReceived( + data_type_state_, UpdateResponseDataList(), UpdateResponseDataList()); } } @@ -144,7 +220,7 @@ void ModelTypeSyncWorkerImpl::EnqueueForCommit( const CommitRequestDataList& list) { DCHECK(CalledOnValidThread()); - DCHECK(CanCommitItems()) + DCHECK(IsTypeInitialized()) << "Asked to commit items before type was initialized. " << "ModelType is: " << ModelTypeToString(type_); @@ -153,6 +229,13 @@ void ModelTypeSyncWorkerImpl::EnqueueForCommit( ++it) { StorePendingCommit(*it); } + + ScopedCryptographerRef scoped_cryptographer_ref; + cryptographer_provider_->InitScopedCryptographerRef( + &scoped_cryptographer_ref); + Cryptographer* cryptographer = scoped_cryptographer_ref.Get(); + if (CanCommitItems(cryptographer)) + nudge_handler_->NudgeForCommit(type_); } // CommitContributor implementation. @@ -164,7 +247,12 @@ scoped_ptr<CommitContribution> ModelTypeSyncWorkerImpl::GetContribution( std::vector<int64> sequence_numbers; google::protobuf::RepeatedPtrField<sync_pb::SyncEntity> commit_entities; - if (!CanCommitItems()) + ScopedCryptographerRef scoped_cryptographer_ref; + cryptographer_provider_->InitScopedCryptographerRef( + &scoped_cryptographer_ref); + Cryptographer* cryptographer = scoped_cryptographer_ref.Get(); + + if (!CanCommitItems(cryptographer)) return scoped_ptr<CommitContribution>(); // TODO(rlarocque): Avoid iterating here. @@ -177,7 +265,7 @@ scoped_ptr<CommitContribution> ModelTypeSyncWorkerImpl::GetContribution( int64 sequence_number = -1; entity->PrepareCommitProto(commit_entity, &sequence_number); - HelpInitializeCommitEntity(commit_entity); + HelpInitializeCommitEntity(cryptographer, commit_entity); sequence_numbers.push_back(sequence_number); space_remaining--; @@ -222,9 +310,6 @@ void ModelTypeSyncWorkerImpl::StorePendingCommit( request.deleted, request.specifics); } - - if (CanCommitItems()) - nudge_handler_->NudgeForCommit(type_); } void ModelTypeSyncWorkerImpl::OnCommitResponse( @@ -260,14 +345,30 @@ base::WeakPtr<ModelTypeSyncWorkerImpl> ModelTypeSyncWorkerImpl::AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } -bool ModelTypeSyncWorkerImpl::CanCommitItems() const { - // We can't commit anything until we know the type's parent node. - // We'll get it in the first update response. +bool ModelTypeSyncWorkerImpl::IsTypeInitialized() const { return !data_type_state_.type_root_id.empty() && data_type_state_.initial_sync_done; } +bool ModelTypeSyncWorkerImpl::CanCommitItems( + Cryptographer* cryptographer) const { + // We can't commit anything until we know the type's parent node. + // We'll get it in the first update response. + if (!IsTypeInitialized()) + return false; + + // Don't commit if we should be encrypting but don't have the required keys. + if (IsEncryptionRequired() && (!cryptographer || !cryptographer->is_ready() || + cryptographer->GetDefaultNigoriKeyName() != + data_type_state_.encryption_key_name)) { + return false; + } + + return true; +} + void ModelTypeSyncWorkerImpl::HelpInitializeCommitEntity( + Cryptographer* cryptographer, sync_pb::SyncEntity* sync_entity) { // Initial commits need our help to generate a client ID. if (!sync_entity->has_id_string()) { @@ -277,14 +378,80 @@ void ModelTypeSyncWorkerImpl::HelpInitializeCommitEntity( base::StringPrintf("%s-%" PRId64, ModelTypeToString(type_), id)); } + // Encrypt the specifics and hide the title if necessary. + if (IsEncryptionRequired()) { + sync_pb::EntitySpecifics encrypted_specifics; + cryptographer->Encrypt(sync_entity->specifics(), + encrypted_specifics.mutable_encrypted()); + sync_entity->mutable_specifics()->CopyFrom(encrypted_specifics); + sync_entity->set_name("encrypted"); + } + // Always include enough specifics to identify the type. Do this even in // deletion requests, where the specifics are otherwise invalid. - if (!sync_entity->has_specifics()) { - AddDefaultFieldValue(type_, sync_entity->mutable_specifics()); - } + AddDefaultFieldValue(type_, sync_entity->mutable_specifics()); // We're always responsible for the parent ID. sync_entity->set_parent_id_string(data_type_state_.type_root_id); } +void ModelTypeSyncWorkerImpl::TryDecryptPendingUpdates() { + UpdateResponseDataList response_datas; + + ScopedCryptographerRef scoped_cryptographer_ref; + cryptographer_provider_->InitScopedCryptographerRef( + &scoped_cryptographer_ref); + Cryptographer* cryptographer = scoped_cryptographer_ref.Get(); + DCHECK(cryptographer); + + for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); + ++it) { + if (it->second->HasPendingUpdate()) { + const UpdateResponseData& saved_pending = it->second->GetPendingUpdate(); + + // We assume all pending updates are encrypted items for which we + // don't have the key. + DCHECK(saved_pending.specifics.has_encrypted()); + + if (cryptographer->CanDecrypt(saved_pending.specifics.encrypted())) { + UpdateResponseData decrypted_response = saved_pending; + if (DecryptSpecifics(cryptographer, + saved_pending.specifics, + &decrypted_response.specifics)) { + decrypted_response.encryption_key_name = + saved_pending.specifics.encrypted().key_name(); + response_datas.push_back(decrypted_response); + + it->second->ClearPendingUpdate(); + } + } + } + } + + if (!response_datas.empty()) { + type_sync_proxy_->OnUpdateReceived( + data_type_state_, response_datas, UpdateResponseDataList()); + } +} + +bool ModelTypeSyncWorkerImpl::DecryptSpecifics( + Cryptographer* cryptographer, + const sync_pb::EntitySpecifics& in, + sync_pb::EntitySpecifics* out) { + DCHECK(in.has_encrypted()); + DCHECK(cryptographer->CanDecrypt(in.encrypted())); + + std::string plaintext; + plaintext = cryptographer->DecryptToString(in.encrypted()); + if (plaintext.empty()) { + LOG(ERROR) << "Failed to decrypt a decryptable entity"; + return false; + } + if (!out->ParseFromString(plaintext)) { + LOG(ERROR) << "Failed to parse decrypted entity"; + return false; + } + return true; +} + } // namespace syncer |