michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: Cu.import("resource://gre/modules/Log.jsm"); michael@0: Cu.import("resource://services-sync/constants.js"); michael@0: Cu.import("resource://services-sync/keys.js"); michael@0: Cu.import("resource://services-sync/record.js"); michael@0: Cu.import("resource://services-sync/resource.js"); michael@0: Cu.import("resource://services-sync/service.js"); michael@0: Cu.import("resource://services-sync/util.js"); michael@0: Cu.import("resource://testing-common/services/sync/utils.js"); michael@0: michael@0: let cryptoWrap; michael@0: michael@0: function crypted_resource_handler(metadata, response) { michael@0: let obj = {id: "resource", michael@0: modified: cryptoWrap.modified, michael@0: payload: JSON.stringify(cryptoWrap.payload)}; michael@0: return httpd_basic_auth_handler(JSON.stringify(obj), metadata, response); michael@0: } michael@0: michael@0: function prepareCryptoWrap(collection, id) { michael@0: let w = new CryptoWrapper(); michael@0: w.cleartext.stuff = "my payload here"; michael@0: w.collection = collection; michael@0: w.id = id; michael@0: return w; michael@0: } michael@0: michael@0: function run_test() { michael@0: let server; michael@0: do_test_pending(); michael@0: michael@0: ensureLegacyIdentityManager(); michael@0: Service.identity.username = "john@example.com"; michael@0: Service.identity.syncKey = "a-abcde-abcde-abcde-abcde-abcde"; michael@0: let keyBundle = Service.identity.syncKeyBundle; michael@0: michael@0: try { michael@0: let log = Log.repository.getLogger("Test"); michael@0: Log.repository.rootLogger.addAppender(new Log.DumpAppender()); michael@0: michael@0: log.info("Setting up server and authenticator"); michael@0: michael@0: server = httpd_setup({"/steam/resource": crypted_resource_handler}); michael@0: michael@0: log.info("Creating a record"); michael@0: michael@0: let cryptoUri = "http://localhost:8080/crypto/steam"; michael@0: cryptoWrap = prepareCryptoWrap("steam", "resource"); michael@0: michael@0: log.info("cryptoWrap: " + cryptoWrap.toString()); michael@0: michael@0: log.info("Encrypting a record"); michael@0: michael@0: cryptoWrap.encrypt(keyBundle); michael@0: log.info("Ciphertext is " + cryptoWrap.ciphertext); michael@0: do_check_true(cryptoWrap.ciphertext != null); michael@0: michael@0: let firstIV = cryptoWrap.IV; michael@0: michael@0: log.info("Decrypting the record"); michael@0: michael@0: let payload = cryptoWrap.decrypt(keyBundle); michael@0: do_check_eq(payload.stuff, "my payload here"); michael@0: do_check_neq(payload, cryptoWrap.payload); // wrap.data.payload is the encrypted one michael@0: michael@0: log.info("Make sure multiple decrypts cause failures"); michael@0: let error = ""; michael@0: try { michael@0: payload = cryptoWrap.decrypt(keyBundle); michael@0: } michael@0: catch(ex) { michael@0: error = ex; michael@0: } michael@0: do_check_eq(error, "No ciphertext: nothing to decrypt?"); michael@0: michael@0: log.info("Re-encrypting the record with alternate payload"); michael@0: michael@0: cryptoWrap.cleartext.stuff = "another payload"; michael@0: cryptoWrap.encrypt(keyBundle); michael@0: let secondIV = cryptoWrap.IV; michael@0: payload = cryptoWrap.decrypt(keyBundle); michael@0: do_check_eq(payload.stuff, "another payload"); michael@0: michael@0: log.info("Make sure multiple encrypts use different IVs"); michael@0: do_check_neq(firstIV, secondIV); michael@0: michael@0: log.info("Make sure differing ids cause failures"); michael@0: cryptoWrap.encrypt(keyBundle); michael@0: cryptoWrap.data.id = "other"; michael@0: error = ""; michael@0: try { michael@0: cryptoWrap.decrypt(keyBundle); michael@0: } michael@0: catch(ex) { michael@0: error = ex; michael@0: } michael@0: do_check_eq(error, "Record id mismatch: resource != other"); michael@0: michael@0: log.info("Make sure wrong hmacs cause failures"); michael@0: cryptoWrap.encrypt(keyBundle); michael@0: cryptoWrap.hmac = "foo"; michael@0: error = ""; michael@0: try { michael@0: cryptoWrap.decrypt(keyBundle); michael@0: } michael@0: catch(ex) { michael@0: error = ex; michael@0: } michael@0: do_check_eq(error.substr(0, 42), "Record SHA256 HMAC mismatch: should be foo"); michael@0: michael@0: // Checking per-collection keys and default key handling. michael@0: michael@0: generateNewKeys(Service.collectionKeys); michael@0: let bu = "http://localhost:8080/storage/bookmarks/foo"; michael@0: let bookmarkItem = prepareCryptoWrap("bookmarks", "foo"); michael@0: bookmarkItem.encrypt(Service.collectionKeys.keyForCollection("bookmarks")); michael@0: log.info("Ciphertext is " + bookmarkItem.ciphertext); michael@0: do_check_true(bookmarkItem.ciphertext != null); michael@0: log.info("Decrypting the record explicitly with the default key."); michael@0: do_check_eq(bookmarkItem.decrypt(Service.collectionKeys._default).stuff, "my payload here"); michael@0: michael@0: // Per-collection keys. michael@0: // Generate a key for "bookmarks". michael@0: generateNewKeys(Service.collectionKeys, ["bookmarks"]); michael@0: bookmarkItem = prepareCryptoWrap("bookmarks", "foo"); michael@0: do_check_eq(bookmarkItem.collection, "bookmarks"); michael@0: michael@0: // Encrypt. This'll use the "bookmarks" encryption key, because we have a michael@0: // special key for it. The same key will need to be used for decryption. michael@0: bookmarkItem.encrypt(Service.collectionKeys.keyForCollection("bookmarks")); michael@0: do_check_true(bookmarkItem.ciphertext != null); michael@0: michael@0: // Attempt to use the default key, because this is a collision that could michael@0: // conceivably occur in the real world. Decryption will error, because michael@0: // it's not the bookmarks key. michael@0: let err; michael@0: try { michael@0: bookmarkItem.decrypt(Service.collectionKeys._default); michael@0: } catch (ex) { michael@0: err = ex; michael@0: } michael@0: do_check_eq("Record SHA256 HMAC mismatch", err.substr(0, 27)); michael@0: michael@0: // Explicitly check that it's using the bookmarks key. michael@0: // This should succeed. michael@0: do_check_eq(bookmarkItem.decrypt(Service.collectionKeys.keyForCollection("bookmarks")).stuff, michael@0: "my payload here"); michael@0: michael@0: log.info("Done!"); michael@0: } michael@0: finally { michael@0: server.stop(do_test_finished); michael@0: } michael@0: }