diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /services/sync/tests/unit/test_corrupt_keys.js | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'services/sync/tests/unit/test_corrupt_keys.js')
-rw-r--r-- | services/sync/tests/unit/test_corrupt_keys.js | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/services/sync/tests/unit/test_corrupt_keys.js b/services/sync/tests/unit/test_corrupt_keys.js new file mode 100644 index 000000000..009461c2a --- /dev/null +++ b/services/sync/tests/unit/test_corrupt_keys.js @@ -0,0 +1,233 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import("resource://gre/modules/PlacesUtils.jsm"); +Cu.import("resource://gre/modules/Log.jsm"); +Cu.import("resource://services-sync/constants.js"); +Cu.import("resource://services-sync/engines.js"); +Cu.import("resource://services-sync/engines/tabs.js"); +Cu.import("resource://services-sync/engines/history.js"); +Cu.import("resource://services-sync/record.js"); +Cu.import("resource://services-sync/service.js"); +Cu.import("resource://services-sync/status.js"); +Cu.import("resource://services-sync/util.js"); +Cu.import("resource://testing-common/services/sync/utils.js"); +Cu.import("resource://gre/modules/Promise.jsm"); + +add_task(function* test_locally_changed_keys() { + let passphrase = "abcdeabcdeabcdeabcdeabcdea"; + + let hmacErrorCount = 0; + function counting(f) { + return function() { + hmacErrorCount++; + return f.call(this); + }; + } + + Service.handleHMACEvent = counting(Service.handleHMACEvent); + + let server = new SyncServer(); + let johndoe = server.registerUser("johndoe", "password"); + johndoe.createContents({ + meta: {}, + crypto: {}, + clients: {} + }); + server.start(); + + try { + Svc.Prefs.set("registerEngines", "Tab"); + _("Set up some tabs."); + let myTabs = + {windows: [{tabs: [{index: 1, + entries: [{ + url: "http://foo.com/", + title: "Title" + }], + attributes: { + image: "image" + } + }]}]}; + delete Svc.Session; + Svc.Session = { + getBrowserState: () => JSON.stringify(myTabs) + }; + + setBasicCredentials("johndoe", "password", passphrase); + Service.serverURL = server.baseURI; + Service.clusterURL = server.baseURI; + + Service.engineManager.register(HistoryEngine); + Service.engineManager.unregister("addons"); + + function corrupt_local_keys() { + Service.collectionKeys._default.keyPair = [Svc.Crypto.generateRandomKey(), + Svc.Crypto.generateRandomKey()]; + } + + _("Setting meta."); + + // Bump version on the server. + let m = new WBORecord("meta", "global"); + m.payload = {"syncID": "foooooooooooooooooooooooooo", + "storageVersion": STORAGE_VERSION}; + m.upload(Service.resource(Service.metaURL)); + + _("New meta/global: " + JSON.stringify(johndoe.collection("meta").wbo("global"))); + + // Upload keys. + generateNewKeys(Service.collectionKeys); + let serverKeys = Service.collectionKeys.asWBO("crypto", "keys"); + serverKeys.encrypt(Service.identity.syncKeyBundle); + do_check_true(serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success); + + // Check that login works. + do_check_true(Service.login("johndoe", "ilovejane", passphrase)); + do_check_true(Service.isLoggedIn); + + // Sync should upload records. + yield sync_and_validate_telem(); + + // Tabs exist. + _("Tabs modified: " + johndoe.modified("tabs")); + do_check_true(johndoe.modified("tabs") > 0); + + let coll_modified = Service.collectionKeys.lastModified; + + // Let's create some server side history records. + let liveKeys = Service.collectionKeys.keyForCollection("history"); + _("Keys now: " + liveKeys.keyPair); + let visitType = Ci.nsINavHistoryService.TRANSITION_LINK; + let history = johndoe.createCollection("history"); + for (let i = 0; i < 5; i++) { + let id = 'record-no--' + i; + let modified = Date.now()/1000 - 60*(i+10); + + let w = new CryptoWrapper("history", "id"); + w.cleartext = { + id: id, + histUri: "http://foo/bar?" + id, + title: id, + sortindex: i, + visits: [{date: (modified - 5) * 1000000, type: visitType}], + deleted: false}; + w.encrypt(liveKeys); + + let payload = {ciphertext: w.ciphertext, + IV: w.IV, + hmac: w.hmac}; + history.insert(id, payload, modified); + } + + history.timestamp = Date.now() / 1000; + let old_key_time = johndoe.modified("crypto"); + _("Old key time: " + old_key_time); + + // Check that we can decrypt one. + let rec = new CryptoWrapper("history", "record-no--0"); + rec.fetch(Service.resource(Service.storageURL + "history/record-no--0")); + _(JSON.stringify(rec)); + do_check_true(!!rec.decrypt(liveKeys)); + + do_check_eq(hmacErrorCount, 0); + + // Fill local key cache with bad data. + corrupt_local_keys(); + _("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair); + + do_check_eq(hmacErrorCount, 0); + + _("HMAC error count: " + hmacErrorCount); + // Now syncing should succeed, after one HMAC error. + let ping = yield wait_for_ping(() => Service.sync(), true); + equal(ping.engines.find(e => e.name == "history").incoming.applied, 5); + + do_check_eq(hmacErrorCount, 1); + _("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair); + + // And look! We downloaded history! + let store = Service.engineManager.get("history")._store; + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--0")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--1")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--2")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--3")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--4")); + do_check_eq(hmacErrorCount, 1); + + _("Busting some new server values."); + // Now what happens if we corrupt the HMAC on the server? + for (let i = 5; i < 10; i++) { + let id = 'record-no--' + i; + let modified = 1 + (Date.now() / 1000); + + let w = new CryptoWrapper("history", "id"); + w.cleartext = { + id: id, + histUri: "http://foo/bar?" + id, + title: id, + sortindex: i, + visits: [{date: (modified - 5 ) * 1000000, type: visitType}], + deleted: false}; + w.encrypt(Service.collectionKeys.keyForCollection("history")); + w.hmac = w.hmac.toUpperCase(); + + let payload = {ciphertext: w.ciphertext, + IV: w.IV, + hmac: w.hmac}; + history.insert(id, payload, modified); + } + history.timestamp = Date.now() / 1000; + + _("Server key time hasn't changed."); + do_check_eq(johndoe.modified("crypto"), old_key_time); + + _("Resetting HMAC error timer."); + Service.lastHMACEvent = 0; + + _("Syncing..."); + ping = yield sync_and_validate_telem(true); + + do_check_eq(ping.engines.find(e => e.name == "history").incoming.failed, 5); + _("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair); + _("Server keys have been updated, and we skipped over 5 more HMAC errors without adjusting history."); + do_check_true(johndoe.modified("crypto") > old_key_time); + do_check_eq(hmacErrorCount, 6); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--5")); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--6")); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--7")); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--8")); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--9")); + } finally { + Svc.Prefs.resetBranch(""); + let deferred = Promise.defer(); + server.stop(deferred.resolve); + yield deferred.promise; + } +}); + +function run_test() { + let logger = Log.repository.rootLogger; + Log.repository.rootLogger.addAppender(new Log.DumpAppender()); + validate_all_future_pings(); + + ensureLegacyIdentityManager(); + + run_next_test(); +} + +/** + * Asynchronously check a url is visited. + * @param url the url + * @return {Promise} + * @resolves When the check has been added successfully. + * @rejects JavaScript exception. + */ +function promiseIsURIVisited(url) { + let deferred = Promise.defer(); + PlacesUtils.asyncHistory.isURIVisited(Utils.makeURI(url), function(aURI, aIsVisited) { + deferred.resolve(aIsVisited); + }); + + return deferred.promise; +} |