services/sync/tests/unit/test_corrupt_keys.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* Any copyright is dedicated to the Public Domain.
     2    http://creativecommons.org/publicdomain/zero/1.0/ */
     4 Cu.import("resource://gre/modules/PlacesUtils.jsm");
     5 Cu.import("resource://gre/modules/Log.jsm");
     6 Cu.import("resource://services-sync/constants.js");
     7 Cu.import("resource://services-sync/engines.js");
     8 Cu.import("resource://services-sync/engines/tabs.js");
     9 Cu.import("resource://services-sync/engines/history.js");
    10 Cu.import("resource://services-sync/record.js");
    11 Cu.import("resource://services-sync/service.js");
    12 Cu.import("resource://services-sync/status.js");
    13 Cu.import("resource://services-sync/util.js");
    14 Cu.import("resource://testing-common/services/sync/utils.js");
    15 Cu.import("resource://gre/modules/Promise.jsm");
    17 add_task(function test_locally_changed_keys() {
    18   let passphrase = "abcdeabcdeabcdeabcdeabcdea";
    20   let hmacErrorCount = 0;
    21   function counting(f) {
    22     return function() {
    23       hmacErrorCount++;
    24       return f.call(this);
    25     };
    26   }
    28   Service.handleHMACEvent = counting(Service.handleHMACEvent);
    30   let server  = new SyncServer();
    31   let johndoe = server.registerUser("johndoe", "password");
    32   johndoe.createContents({
    33     meta: {},
    34     crypto: {},
    35     clients: {}
    36   });
    37   server.start();
    39   try {
    40     Svc.Prefs.set("registerEngines", "Tab");
    41     _("Set up some tabs.");
    42     let myTabs =
    43       {windows: [{tabs: [{index: 1,
    44                           entries: [{
    45                             url: "http://foo.com/",
    46                             title: "Title"
    47                           }],
    48                           attributes: {
    49                             image: "image"
    50                           }
    51                           }]}]};
    52     delete Svc.Session;
    53     Svc.Session = {
    54       getBrowserState: function () JSON.stringify(myTabs)
    55     };
    57     setBasicCredentials("johndoe", "password", passphrase);
    58     Service.serverURL = server.baseURI;
    59     Service.clusterURL = server.baseURI;
    61     Service.engineManager.register(HistoryEngine);
    63     function corrupt_local_keys() {
    64       Service.collectionKeys._default.keyPair = [Svc.Crypto.generateRandomKey(),
    65                                                  Svc.Crypto.generateRandomKey()];
    66     }
    68     _("Setting meta.");
    70     // Bump version on the server.
    71     let m = new WBORecord("meta", "global");
    72     m.payload = {"syncID": "foooooooooooooooooooooooooo",
    73                  "storageVersion": STORAGE_VERSION};
    74     m.upload(Service.resource(Service.metaURL));
    76     _("New meta/global: " + JSON.stringify(johndoe.collection("meta").wbo("global")));
    78     // Upload keys.
    79     generateNewKeys(Service.collectionKeys);
    80     let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
    81     serverKeys.encrypt(Service.identity.syncKeyBundle);
    82     do_check_true(serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success);
    84     // Check that login works.
    85     do_check_true(Service.login("johndoe", "ilovejane", passphrase));
    86     do_check_true(Service.isLoggedIn);
    88     // Sync should upload records.
    89     Service.sync();
    91     // Tabs exist.
    92     _("Tabs modified: " + johndoe.modified("tabs"));
    93     do_check_true(johndoe.modified("tabs") > 0);
    95     let coll_modified = Service.collectionKeys.lastModified;
    97     // Let's create some server side history records.
    98     let liveKeys = Service.collectionKeys.keyForCollection("history");
    99     _("Keys now: " + liveKeys.keyPair);
   100     let visitType = Ci.nsINavHistoryService.TRANSITION_LINK;
   101     let history   = johndoe.createCollection("history");
   102     for (let i = 0; i < 5; i++) {
   103       let id = 'record-no--' + i;
   104       let modified = Date.now()/1000 - 60*(i+10);
   106       let w = new CryptoWrapper("history", "id");
   107       w.cleartext = {
   108         id: id,
   109         histUri: "http://foo/bar?" + id,
   110         title: id,
   111         sortindex: i,
   112         visits: [{date: (modified - 5) * 1000000, type: visitType}],
   113         deleted: false};
   114       w.encrypt(liveKeys);
   116       let payload = {ciphertext: w.ciphertext,
   117                      IV:         w.IV,
   118                      hmac:       w.hmac};
   119       history.insert(id, payload, modified);
   120     }
   122     history.timestamp = Date.now() / 1000;
   123     let old_key_time = johndoe.modified("crypto");
   124     _("Old key time: " + old_key_time);
   126     // Check that we can decrypt one.
   127     let rec = new CryptoWrapper("history", "record-no--0");
   128     rec.fetch(Service.resource(Service.storageURL + "history/record-no--0"));
   129     _(JSON.stringify(rec));
   130     do_check_true(!!rec.decrypt(liveKeys));
   132     do_check_eq(hmacErrorCount, 0);
   134     // Fill local key cache with bad data.
   135     corrupt_local_keys();
   136     _("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair);
   138     do_check_eq(hmacErrorCount, 0);
   140     _("HMAC error count: " + hmacErrorCount);
   141     // Now syncing should succeed, after one HMAC error.
   142     Service.sync();
   143     do_check_eq(hmacErrorCount, 1);
   144     _("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair);
   146     // And look! We downloaded history!
   147     let store = Service.engineManager.get("history")._store;
   148     do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--0"));
   149     do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--1"));
   150     do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--2"));
   151     do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--3"));
   152     do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--4"));
   153     do_check_eq(hmacErrorCount, 1);
   155     _("Busting some new server values.");
   156     // Now what happens if we corrupt the HMAC on the server?
   157     for (let i = 5; i < 10; i++) {
   158       let id = 'record-no--' + i;
   159       let modified = 1 + (Date.now() / 1000);
   161       let w = new CryptoWrapper("history", "id");
   162       w.cleartext = {
   163         id: id,
   164         histUri: "http://foo/bar?" + id,
   165         title: id,
   166         sortindex: i,
   167         visits: [{date: (modified - 5 ) * 1000000, type: visitType}],
   168         deleted: false};
   169       w.encrypt(Service.collectionKeys.keyForCollection("history"));
   170       w.hmac = w.hmac.toUpperCase();
   172       let payload = {ciphertext: w.ciphertext,
   173                      IV:         w.IV,
   174                      hmac:       w.hmac};
   175       history.insert(id, payload, modified);
   176     }
   177     history.timestamp = Date.now() / 1000;
   179     _("Server key time hasn't changed.");
   180     do_check_eq(johndoe.modified("crypto"), old_key_time);
   182     _("Resetting HMAC error timer.");
   183     Service.lastHMACEvent = 0;
   185     _("Syncing...");
   186     Service.sync();
   187     _("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair);
   188     _("Server keys have been updated, and we skipped over 5 more HMAC errors without adjusting history.");
   189     do_check_true(johndoe.modified("crypto") > old_key_time);
   190     do_check_eq(hmacErrorCount, 6);
   191     do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--5"));
   192     do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--6"));
   193     do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--7"));
   194     do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--8"));
   195     do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--9"));
   196   } finally {
   197     Svc.Prefs.resetBranch("");
   198     let deferred = Promise.defer();
   199     server.stop(deferred.resolve);
   200     yield deferred.promise;
   201   }
   202 });
   204 function run_test() {
   205   let logger = Log.repository.rootLogger;
   206   Log.repository.rootLogger.addAppender(new Log.DumpAppender());
   208   ensureLegacyIdentityManager();
   210   run_next_test();
   211 }
   213 /**
   214  * Asynchronously check a url is visited.
   215  * @param url the url
   216  * @return {Promise}
   217  * @resolves When the check has been added successfully.
   218  * @rejects JavaScript exception.
   219  */
   220 function promiseIsURIVisited(url) {
   221   let deferred = Promise.defer();
   222   PlacesUtils.asyncHistory.isURIVisited(Utils.makeURI(url), function(aURI, aIsVisited) {
   223     deferred.resolve(aIsVisited);
   224   });
   226   return deferred.promise;
   227 }

mercurial