summaryrefslogtreecommitdiffstats
path: root/services/sync/tests/unit/test_records_crypto.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/sync/tests/unit/test_records_crypto.js')
-rw-r--r--services/sync/tests/unit/test_records_crypto.js182
1 files changed, 182 insertions, 0 deletions
diff --git a/services/sync/tests/unit/test_records_crypto.js b/services/sync/tests/unit/test_records_crypto.js
new file mode 100644
index 000000000..392a746ef
--- /dev/null
+++ b/services/sync/tests/unit/test_records_crypto.js
@@ -0,0 +1,182 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/keys.js");
+Cu.import("resource://services-sync/record.js");
+Cu.import("resource://services-sync/resource.js");
+Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/util.js");
+Cu.import("resource://testing-common/services/sync/utils.js");
+
+var cryptoWrap;
+
+function crypted_resource_handler(metadata, response) {
+ let obj = {id: "resource",
+ modified: cryptoWrap.modified,
+ payload: JSON.stringify(cryptoWrap.payload)};
+ return httpd_basic_auth_handler(JSON.stringify(obj), metadata, response);
+}
+
+function prepareCryptoWrap(collection, id) {
+ let w = new CryptoWrapper();
+ w.cleartext.stuff = "my payload here";
+ w.collection = collection;
+ w.id = id;
+ return w;
+}
+
+function run_test() {
+ let server;
+ do_test_pending();
+
+ ensureLegacyIdentityManager();
+ Service.identity.username = "john@example.com";
+ Service.identity.syncKey = "a-abcde-abcde-abcde-abcde-abcde";
+ let keyBundle = Service.identity.syncKeyBundle;
+
+ try {
+ let log = Log.repository.getLogger("Test");
+ Log.repository.rootLogger.addAppender(new Log.DumpAppender());
+
+ log.info("Setting up server and authenticator");
+
+ server = httpd_setup({"/steam/resource": crypted_resource_handler});
+
+ log.info("Creating a record");
+
+ let cryptoUri = "http://localhost:8080/crypto/steam";
+ cryptoWrap = prepareCryptoWrap("steam", "resource");
+
+ log.info("cryptoWrap: " + cryptoWrap.toString());
+
+ log.info("Encrypting a record");
+
+ cryptoWrap.encrypt(keyBundle);
+ log.info("Ciphertext is " + cryptoWrap.ciphertext);
+ do_check_true(cryptoWrap.ciphertext != null);
+
+ let firstIV = cryptoWrap.IV;
+
+ log.info("Decrypting the record");
+
+ let payload = cryptoWrap.decrypt(keyBundle);
+ do_check_eq(payload.stuff, "my payload here");
+ do_check_neq(payload, cryptoWrap.payload); // wrap.data.payload is the encrypted one
+
+ log.info("Make sure multiple decrypts cause failures");
+ let error = "";
+ try {
+ payload = cryptoWrap.decrypt(keyBundle);
+ }
+ catch(ex) {
+ error = ex;
+ }
+ do_check_eq(error, "No ciphertext: nothing to decrypt?");
+
+ log.info("Re-encrypting the record with alternate payload");
+
+ cryptoWrap.cleartext.stuff = "another payload";
+ cryptoWrap.encrypt(keyBundle);
+ let secondIV = cryptoWrap.IV;
+ payload = cryptoWrap.decrypt(keyBundle);
+ do_check_eq(payload.stuff, "another payload");
+
+ log.info("Make sure multiple encrypts use different IVs");
+ do_check_neq(firstIV, secondIV);
+
+ log.info("Make sure differing ids cause failures");
+ cryptoWrap.encrypt(keyBundle);
+ cryptoWrap.data.id = "other";
+ error = "";
+ try {
+ cryptoWrap.decrypt(keyBundle);
+ }
+ catch(ex) {
+ error = ex;
+ }
+ do_check_eq(error, "Record id mismatch: resource != other");
+
+ log.info("Make sure wrong hmacs cause failures");
+ cryptoWrap.encrypt(keyBundle);
+ cryptoWrap.hmac = "foo";
+ error = "";
+ try {
+ cryptoWrap.decrypt(keyBundle);
+ }
+ catch(ex) {
+ error = ex;
+ }
+ do_check_eq(error.substr(0, 42), "Record SHA256 HMAC mismatch: should be foo");
+
+ // Checking per-collection keys and default key handling.
+
+ generateNewKeys(Service.collectionKeys);
+ let bu = "http://localhost:8080/storage/bookmarks/foo";
+ let bookmarkItem = prepareCryptoWrap("bookmarks", "foo");
+ bookmarkItem.encrypt(Service.collectionKeys.keyForCollection("bookmarks"));
+ log.info("Ciphertext is " + bookmarkItem.ciphertext);
+ do_check_true(bookmarkItem.ciphertext != null);
+ log.info("Decrypting the record explicitly with the default key.");
+ do_check_eq(bookmarkItem.decrypt(Service.collectionKeys._default).stuff, "my payload here");
+
+ // Per-collection keys.
+ // Generate a key for "bookmarks".
+ generateNewKeys(Service.collectionKeys, ["bookmarks"]);
+ bookmarkItem = prepareCryptoWrap("bookmarks", "foo");
+ do_check_eq(bookmarkItem.collection, "bookmarks");
+
+ // Encrypt. This'll use the "bookmarks" encryption key, because we have a
+ // special key for it. The same key will need to be used for decryption.
+ bookmarkItem.encrypt(Service.collectionKeys.keyForCollection("bookmarks"));
+ do_check_true(bookmarkItem.ciphertext != null);
+
+ // Attempt to use the default key, because this is a collision that could
+ // conceivably occur in the real world. Decryption will error, because
+ // it's not the bookmarks key.
+ let err;
+ try {
+ bookmarkItem.decrypt(Service.collectionKeys._default);
+ } catch (ex) {
+ err = ex;
+ }
+ do_check_eq("Record SHA256 HMAC mismatch", err.substr(0, 27));
+
+ // Explicitly check that it's using the bookmarks key.
+ // This should succeed.
+ do_check_eq(bookmarkItem.decrypt(Service.collectionKeys.keyForCollection("bookmarks")).stuff,
+ "my payload here");
+
+ do_check_true(Service.collectionKeys.hasKeysFor(["bookmarks"]));
+
+ // Add a key for some new collection and verify that it isn't the
+ // default key.
+ do_check_false(Service.collectionKeys.hasKeysFor(["forms"]));
+ do_check_false(Service.collectionKeys.hasKeysFor(["bookmarks", "forms"]));
+ let oldFormsKey = Service.collectionKeys.keyForCollection("forms");
+ do_check_eq(oldFormsKey, Service.collectionKeys._default);
+ let newKeys = Service.collectionKeys.ensureKeysFor(["forms"]);
+ do_check_true(newKeys.hasKeysFor(["forms"]));
+ do_check_true(newKeys.hasKeysFor(["bookmarks", "forms"]));
+ let newFormsKey = newKeys.keyForCollection("forms");
+ do_check_neq(newFormsKey, oldFormsKey);
+
+ // Verify that this doesn't overwrite keys
+ let regetKeys = newKeys.ensureKeysFor(["forms"]);
+ do_check_eq(regetKeys.keyForCollection("forms"), newFormsKey);
+
+ const emptyKeys = new CollectionKeyManager();
+ payload = {
+ default: Service.collectionKeys._default.keyPairB64,
+ collections: {}
+ };
+ // Verify that not passing `modified` doesn't throw
+ emptyKeys.setContents(payload, null);
+
+ log.info("Done!");
+ }
+ finally {
+ server.stop(do_test_finished);
+ }
+}