Skip to content

Commit

Permalink
sync: Add non-blocking type encryption support
Browse files Browse the repository at this point in the history
Introduces the framework for dealing with sync encryption in
non-blocking types.  Unlike directory sync types, non-blocking type
encryption only encrypts data before it is sent to the server.
Encrypting the data on-disk is a separate problem.

Adds code to the ModelTypeSyncWorker so it can access the directory's
cryptographer (through a CryptographerProvider interface) and use it to
encrypt entities before it sends them to the server.  If the
cryptographer is unable to encrypt with the desired key, the worker will
not commit until the cryptographer returns to a good state.

Adds the concept of a "desired encryption key" to the data type state.
When the cryptographer key to be used to encrypt a type changes, this
will be reflected in the data type state.  The ModelTypeSyncProxy is
responsible for ensuring that all items which have not yet been
encrypted with this desired key are enqueued for commit.

Makes the ModelTypeSyncWorker, EntityTracker, and ModelTypeSyncProxy
collaborate on the management of undecryptable (inapplicable) updates.
The EntityTracker keeps track of their version numbers and content, and
prevents the committing of new items to the server until the
inapplicable update has been dealt with.  The ModelTypeSyncProxy is
responsible for saving inapplicable updates across restarts.

This CL alone is not enough to enable encryption support for
non-blocking types.  It requires additional code to hook up the
ModelTypeSyncWorkers to receive cryptographer events.  This will be
added in a future commit.  In the meantime, this CL includes plenty
of unit tests to verify the functionality that's being added.

BUG=351005

Review URL: https://codereview.chromium.org/423193002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287428 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
rlarocque@chromium.org committed Aug 5, 2014
1 parent 87a0a99 commit c44d7f0
Show file tree
Hide file tree
Showing 32 changed files with 1,244 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class MockSyncContextProxy : public syncer::SyncContextProxy {
virtual void ConnectTypeToSync(
syncer::ModelType type,
const syncer::DataTypeState& data_type_state,
const syncer::UpdateResponseDataList& saved_pending_updates,
const base::WeakPtr<syncer::ModelTypeSyncProxyImpl>& type_proxy)
OVERRIDE {
// Normally we'd use MessageLoopProxy::current() as the TaskRunner argument
Expand Down
35 changes: 33 additions & 2 deletions sync/engine/entity_tracker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,14 @@ void EntityTracker::ReceiveCommitResponse(const std::string& response_id,
}

void EntityTracker::ReceiveUpdate(int64 version) {
highest_gu_response_version_ =
std::max(highest_gu_response_version_, version);
if (version <= highest_gu_response_version_)
return;

highest_gu_response_version_ = version;

// Got an applicable update newer than any pending updates. It must be safe
// to discard the old pending update, if there was one.
ClearPendingUpdate();

if (IsInConflict()) {
// Incoming update clobbers the pending commit on the sync thread.
Expand All @@ -203,10 +209,35 @@ void EntityTracker::ReceiveUpdate(int64 version) {
}
}

bool EntityTracker::ReceivePendingUpdate(const UpdateResponseData& data) {
if (data.response_version < highest_gu_response_version_)
return false;

highest_gu_response_version_ = data.response_version;
pending_update_.reset(new UpdateResponseData(data));
ClearPendingCommit();
return true;
}

bool EntityTracker::HasPendingUpdate() const {
return !!pending_update_;
}

UpdateResponseData EntityTracker::GetPendingUpdate() const {
return *pending_update_;
}

void EntityTracker::ClearPendingUpdate() {
pending_update_.reset();
}

bool EntityTracker::IsInConflict() const {
if (!is_commit_pending_)
return false;

if (HasPendingUpdate())
return true;

if (highest_gu_response_version_ <= highest_commit_response_version_) {
// The most recent server state was created in a commit made by this
// client. We're fully up to date, and therefore not in conflict.
Expand Down
21 changes: 21 additions & 0 deletions sync/engine/entity_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
#include <string>

#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "sync/base/sync_export.h"
#include "sync/internal_api/public/non_blocking_sync_common.h"
#include "sync/protocol/sync.pb.h"

namespace syncer {
Expand Down Expand Up @@ -81,6 +83,20 @@ class SYNC_EXPORT EntityTracker {
// Handles receipt of an update from the server.
void ReceiveUpdate(int64 version);

// Handles the receipt of an pending update from the server.
//
// Returns true if the tracker decides this item is worth keeping. Returns
// false if the item is discarded, which could happen if the version number
// is out of date.
bool ReceivePendingUpdate(const UpdateResponseData& data);

// Functions to fetch the latest pending update.
bool HasPendingUpdate() const;
UpdateResponseData GetPendingUpdate() const;

// Clears the pending update. Allows us to resume regular commit behavior.
void ClearPendingUpdate();

private:
// Initializes received update state. Does not initialize state related to
// pending commits and sets |is_commit_pending_| to false.
Expand Down Expand Up @@ -146,6 +162,11 @@ class SYNC_EXPORT EntityTracker {
bool deleted_;
sync_pb::EntitySpecifics specifics_;

// An update for this item which can't be applied right now. The presence of
// an pending update prevents commits. As of this writing, the only source
// of pending updates is updates we can't decrypt right now.
scoped_ptr<UpdateResponseData> pending_update_;

DISALLOW_COPY_AND_ASSIGN(EntityTracker);
};

Expand Down
36 changes: 27 additions & 9 deletions sync/engine/model_type_entity.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ scoped_ptr<ModelTypeEntity> ModelTypeEntity::NewLocalItem(
specifics,
false,
now,
now));
now,
std::string()));
}

scoped_ptr<ModelTypeEntity> ModelTypeEntity::FromServerUpdate(
Expand All @@ -35,7 +36,8 @@ scoped_ptr<ModelTypeEntity> ModelTypeEntity::FromServerUpdate(
const sync_pb::EntitySpecifics& specifics,
bool deleted,
base::Time ctime,
base::Time mtime) {
base::Time mtime,
const std::string& encryption_key_name) {
return scoped_ptr<ModelTypeEntity>(new ModelTypeEntity(0,
0,
0,
Expand All @@ -47,7 +49,8 @@ scoped_ptr<ModelTypeEntity> ModelTypeEntity::FromServerUpdate(
specifics,
deleted,
ctime,
mtime));
mtime,
encryption_key_name));
}

ModelTypeEntity::ModelTypeEntity(int64 sequence_number,
Expand All @@ -61,7 +64,8 @@ ModelTypeEntity::ModelTypeEntity(int64 sequence_number,
const sync_pb::EntitySpecifics& specifics,
bool deleted,
base::Time ctime,
base::Time mtime)
base::Time mtime,
const std::string& encryption_key_name)
: sequence_number_(sequence_number),
commit_requested_sequence_number_(commit_requested_sequence_number),
acked_sequence_number_(acked_sequence_number),
Expand All @@ -73,7 +77,8 @@ ModelTypeEntity::ModelTypeEntity(int64 sequence_number,
specifics_(specifics),
deleted_(deleted),
ctime_(ctime),
mtime_(mtime) {
mtime_(mtime),
encryption_key_name_(encryption_key_name) {
}

ModelTypeEntity::~ModelTypeEntity() {
Expand Down Expand Up @@ -103,7 +108,8 @@ void ModelTypeEntity::ApplyUpdateFromServer(
int64 update_version,
bool deleted,
const sync_pb::EntitySpecifics& specifics,
base::Time mtime) {
base::Time mtime,
const std::string& encryption_key_name) {
// There was a conflict and the server just won it.
// This implicitly acks all outstanding commits because a received update
// will clobber any pending commits on the sync thread.
Expand All @@ -121,6 +127,15 @@ void ModelTypeEntity::MakeLocalChange(
specifics_ = specifics;
}

void ModelTypeEntity::UpdateDesiredEncryptionKey(const std::string& name) {
if (encryption_key_name_ == name)
return;

// Schedule commit with the expectation that the worker will re-encrypt with
// the latest encryption key as it does.
sequence_number_++;
}

void ModelTypeEntity::Delete() {
sequence_number_++;
specifics_.Clear();
Expand All @@ -144,12 +159,15 @@ void ModelTypeEntity::SetCommitRequestInProgress() {
commit_requested_sequence_number_ = sequence_number_;
}

void ModelTypeEntity::ReceiveCommitResponse(const std::string& id,
int64 sequence_number,
int64 response_version) {
void ModelTypeEntity::ReceiveCommitResponse(
const std::string& id,
int64 sequence_number,
int64 response_version,
const std::string& encryption_key_name) {
id_ = id; // The server can assign us a new ID in a commit response.
acked_sequence_number_ = sequence_number;
base_version_ = response_version;
encryption_key_name_ = encryption_key_name;
}

void ModelTypeEntity::ClearTransientSyncState() {
Expand Down
21 changes: 17 additions & 4 deletions sync/engine/model_type_entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class SYNC_EXPORT_PRIVATE ModelTypeEntity {
const sync_pb::EntitySpecifics& specifics,
bool deleted,
base::Time ctime,
base::Time mtime);
base::Time mtime,
const std::string& encryption_key_name);

// TODO(rlarocque): Implement FromDisk constructor when we implement storage.

Expand Down Expand Up @@ -79,11 +80,17 @@ class SYNC_EXPORT_PRIVATE ModelTypeEntity {
void ApplyUpdateFromServer(int64 update_version,
bool deleted,
const sync_pb::EntitySpecifics& specifics,
base::Time mtime);
base::Time mtime,
const std::string& encryption_key_name);

// Applies a local change to this item.
void MakeLocalChange(const sync_pb::EntitySpecifics& specifics);

// Schedule a commit if the |name| does not match this item's last known
// encryption key. The worker that performs the commit is expected to
// encrypt the item using the latest available key.
void UpdateDesiredEncryptionKey(const std::string& name);

// Applies a local deletion to this item.
void Delete();

Expand All @@ -104,7 +111,8 @@ class SYNC_EXPORT_PRIVATE ModelTypeEntity {
// reached the server.
void ReceiveCommitResponse(const std::string& id,
int64 sequence_number,
int64 response_version);
int64 response_version,
const std::string& encryption_key_name);

// Clears any in-memory sync state associated with outstanding commits.
void ClearTransientSyncState();
Expand All @@ -124,7 +132,8 @@ class SYNC_EXPORT_PRIVATE ModelTypeEntity {
const sync_pb::EntitySpecifics& specifics,
bool deleted,
base::Time ctime,
base::Time mtime);
base::Time mtime,
const std::string& encryption_key_name);

// A sequence number used to track in-progress commits. Each local change
// increments this number.
Expand Down Expand Up @@ -185,6 +194,10 @@ class SYNC_EXPORT_PRIVATE ModelTypeEntity {
// doesn't bother to inspect their values.
base::Time ctime_;
base::Time mtime_;

// The name of the encryption key used to encrypt this item on the server.
// Empty when no encryption is in use.
std::string encryption_key_name_;
};

} // namespace syncer
Expand Down
18 changes: 12 additions & 6 deletions sync/engine/model_type_entity_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ TEST_F(ModelTypeEntityTest, FromServerUpdate) {
specifics,
false,
kCtime,
kMtime));
kMtime,
std::string()));

EXPECT_TRUE(entity->IsWriteRequired());
EXPECT_FALSE(entity->IsUnsynced());
Expand All @@ -87,7 +88,8 @@ TEST_F(ModelTypeEntityTest, TombstoneUpdate) {
sync_pb::EntitySpecifics(),
true,
kCtime,
kMtime));
kMtime,
std::string()));

EXPECT_TRUE(entity->IsWriteRequired());
EXPECT_FALSE(entity->IsUnsynced());
Expand All @@ -107,13 +109,15 @@ TEST_F(ModelTypeEntityTest, ApplyUpdate) {
specifics,
false,
kCtime,
kMtime));
kMtime,
std::string()));

// A deletion update one version later.
entity->ApplyUpdateFromServer(11,
true,
sync_pb::EntitySpecifics(),
kMtime + base::TimeDelta::FromSeconds(10));
kMtime + base::TimeDelta::FromSeconds(10),
std::string());

EXPECT_TRUE(entity->IsWriteRequired());
EXPECT_FALSE(entity->IsUnsynced());
Expand All @@ -130,7 +134,8 @@ TEST_F(ModelTypeEntityTest, LocalChange) {
specifics,
false,
kCtime,
kMtime));
kMtime,
std::string()));

sync_pb::EntitySpecifics specifics2;
specifics2.CopyFrom(specifics);
Expand All @@ -156,7 +161,8 @@ TEST_F(ModelTypeEntityTest, LocalDeletion) {
specifics,
false,
kCtime,
kMtime));
kMtime,
std::string()));

entity->Delete();

Expand Down
3 changes: 2 additions & 1 deletion sync/engine/model_type_sync_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class SYNC_EXPORT_PRIVATE ModelTypeSyncProxy {
const CommitResponseDataList& response_list) = 0;
virtual void OnUpdateReceived(
const DataTypeState& type_state,
const UpdateResponseDataList& response_list) = 0;
const UpdateResponseDataList& response_list,
const UpdateResponseDataList& pending_updates) = 0;
};

} // namespace syncer
Expand Down
Loading

0 comments on commit c44d7f0

Please sign in to comment.