diff options
author | zea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-19 01:56:52 +0000 |
---|---|---|
committer | zea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-19 01:56:52 +0000 |
commit | 9a3072c62ef8896a3e089b50efff8f2eb6794fcd (patch) | |
tree | 123be8e004986b76bc322e37b3222ec2712313b4 /net/tools | |
parent | 270f2cf4ed76055f185ca9c59d3e4b9316b758fc (diff) | |
download | chromium_src-9a3072c62ef8896a3e089b50efff8f2eb6794fcd.zip chromium_src-9a3072c62ef8896a3e089b50efff8f2eb6794fcd.tar.gz chromium_src-9a3072c62ef8896a3e089b50efff8f2eb6794fcd.tar.bz2 |
[Sync] Add support for keystore key rotation.
Key rotation will trigger a full re-encryption of all sync data with the newest
keystore key. Previous keys will be added to the keybag as well. We detect key
rotation by checking whether we have multiple keystore keys and the nigori's
keybag is not encrypted with the current keystore key. In that case, we no
longer support backwards compatibility with non-keystore supporting versions,
and re-encrypt using the newest keystore key.
This change also fixes two latent issues: lack of encryption of the keystore
bootstrap and not properly posting OnPassphraseAccepted when we resolve pending
keys.
BUG=163744
Review URL: https://chromiumcodereview.appspot.com/11434070
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173830 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/tools')
-rw-r--r-- | net/tools/testserver/chromiumsync.py | 49 | ||||
-rwxr-xr-x | net/tools/testserver/chromiumsync_test.py | 23 | ||||
-rwxr-xr-x | net/tools/testserver/testserver.py | 16 |
3 files changed, 76 insertions, 12 deletions
diff --git a/net/tools/testserver/chromiumsync.py b/net/tools/testserver/chromiumsync.py index 8e25482..789b821 100644 --- a/net/tools/testserver/chromiumsync.py +++ b/net/tools/testserver/chromiumsync.py @@ -224,6 +224,12 @@ def DataTypeStringToSyncTypeLoose(data_type_string): raise DataTypeIdNotRecognized +def MakeNewKeystoreKey(): + """Returns a new random keystore key.""" + return ''.join(random.choice(string.ascii_uppercase + string.digits) + for x in xrange(KEYSTORE_KEY_LENGTH)) + + def SyncTypeToString(data_type): """Formats a sync type enum (from ALL_TYPES) to a human-readable string.""" return SYNC_TYPE_TO_DESCRIPTOR[data_type].name @@ -490,8 +496,7 @@ class SyncDataModel(object): self.induced_error_frequency = 0 self.sync_count_before_errors = 0 - self._key = ''.join(random.choice(string.ascii_uppercase + string.digits) - for x in xrange(KEYSTORE_KEY_LENGTH)) + self._keys = [MakeNewKeystoreKey()] def _SaveEntry(self, entry): """Insert or update an entry in the change log, and give it a new version. @@ -689,10 +694,10 @@ class SyncDataModel(object): # batch, even if that item was filtered out. return (batch[-1].version, filtered, len(new_changes) - len(batch)) - def GetKey(self): - """Returns the encryption key for this account.""" - print "Returning encryption key: %s" % self._key - return self._key + def GetKeystoreKeys(self): + """Returns the encryption keys for this account.""" + print "Returning encryption keys: %s" % self._keys + return self._keys def _CopyOverImmutableFields(self, entry): """Preserve immutable fields by copying pre-commit state. @@ -1005,6 +1010,23 @@ class SyncDataModel(object): self._SaveEntry(keystore_entry) + def TriggerRotateKeystoreKeys(self): + """Rotate the current set of keystore encryption keys. + + |self._keys| will have a new random encryption key appended to it. We touch + the nigori node so that each client will receive the new encryption keys + only once. + """ + + # Add a new encryption key. + self._keys += [MakeNewKeystoreKey(), ] + + # Increment the nigori node's timestamp, so clients will get the new keys + # on their next GetUpdates (any time the nigori node is sent back, we also + # send back the keystore keys). + nigori_tag = "google_chrome_nigori" + self._SaveEntry(self._entries.get(self._ServerTagToId(nigori_tag))) + def SetInducedError(self, error, error_frequency, sync_count_before_errors): self.induced_error = error @@ -1169,6 +1191,14 @@ class TestServer(object): '<html><title>Enable Keystore Encryption</title>' '<H1>Enable Keystore Encryption</H1></html>') + def HandleRotateKeystoreKeys(self): + """Rotate the keystore encryption keys.""" + self.account.TriggerRotateKeystoreKeys() + return ( + 200, + '<html><title>Rotate Keystore Keys</title>' + '<H1>Rotate Keystore Keys</H1></html>') + def HandleCommand(self, query, raw_request): """Decode and handle a sync command from a raw input of bytes. @@ -1324,10 +1354,13 @@ class TestServer(object): new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) update_response.changes_remaining = remaining + sending_nigori_node = False for entry in entries: + if entry.name == 'Nigori': + sending_nigori_node = True reply = update_response.entries.add() reply.CopyFrom(entry) update_sieve.SaveProgress(new_timestamp, update_response) - if update_request.need_encryption_key: - update_response.encryption_key = self.account.GetKey() + if update_request.need_encryption_key or sending_nigori_node: + update_response.encryption_keys.extend(self.account.GetKeystoreKeys()) diff --git a/net/tools/testserver/chromiumsync_test.py b/net/tools/testserver/chromiumsync_test.py index 56970b3..2b6f0d9 100755 --- a/net/tools/testserver/chromiumsync_test.py +++ b/net/tools/testserver/chromiumsync_test.py @@ -599,11 +599,28 @@ class SyncDataModelTest(unittest.TestCase): self.assertEqual(version2, version) def testGetKey(self): - key1 = self.model.GetKey() - key2 = self.model.GetKey() - self.assertTrue(len(key1) > 0) + [key1] = self.model.GetKeystoreKeys() + [key2] = self.model.GetKeystoreKeys() + self.assertTrue(len(key1)) self.assertEqual(key1, key2) + # Trigger the rotation. A subsequent GetUpdates should return the nigori + # node (whose timestamp was bumped by the rotation). + version1, changes, remaining = ( + self.GetChangesFromTimestamp([chromiumsync.NIGORI], 0)) + self.model.TriggerRotateKeystoreKeys() + version2, changes, remaining = ( + self.GetChangesFromTimestamp([chromiumsync.NIGORI], version1)) + self.assertNotEqual(version1, version2) + self.assertEquals(len(changes), 1) + self.assertEquals(changes[0].name, "Nigori") + + # The current keys should contain the old keys, with the new key appended. + [key1, key3] = self.model.GetKeystoreKeys() + self.assertEquals(key1, key2) + self.assertNotEqual(key1, key3) + self.assertTrue(len(key3) > 0) + def testTriggerEnableKeystoreEncryption(self): version1, changes, remaining = ( self.GetChangesFromTimestamp([chromiumsync.EXPERIMENTS], 0)) diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py index 139724c..91fe6e1 100755 --- a/net/tools/testserver/testserver.py +++ b/net/tools/testserver/testserver.py @@ -1739,7 +1739,8 @@ class SyncPageHandler(BasePageHandler): self.ChromiumSyncErrorOpHandler, self.ChromiumSyncSyncTabFaviconsOpHandler, self.ChromiumSyncCreateSyncedBookmarksOpHandler, - self.ChromiumSyncEnableKeystoreEncryptionOpHandler] + self.ChromiumSyncEnableKeystoreEncryptionOpHandler, + self.ChromiumSyncRotateKeystoreKeysOpHandler] post_handlers = [self.ChromiumSyncCommandHandler, self.ChromiumSyncTimeHandler] @@ -1990,6 +1991,19 @@ class SyncPageHandler(BasePageHandler): self.wfile.write(raw_reply) return True + def ChromiumSyncRotateKeystoreKeysOpHandler(self): + test_name = "/chromiumsync/rotatekeystorekeys" + if not self._ShouldHandleRequest(test_name): + return False + result, raw_reply = ( + self.server._sync_handler.HandleRotateKeystoreKeys()) + self.send_response(result) + self.send_header('Content-Type', 'text/html') + self.send_header('Content-Length', len(raw_reply)) + self.end_headers() + self.wfile.write(raw_reply) + return True + class OCSPHandler(BasePageHandler): def __init__(self, request, client_address, socket_server): |