1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/tests/unit/test_records_crypto.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,156 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +Cu.import("resource://gre/modules/Log.jsm"); 1.8 +Cu.import("resource://services-sync/constants.js"); 1.9 +Cu.import("resource://services-sync/keys.js"); 1.10 +Cu.import("resource://services-sync/record.js"); 1.11 +Cu.import("resource://services-sync/resource.js"); 1.12 +Cu.import("resource://services-sync/service.js"); 1.13 +Cu.import("resource://services-sync/util.js"); 1.14 +Cu.import("resource://testing-common/services/sync/utils.js"); 1.15 + 1.16 +let cryptoWrap; 1.17 + 1.18 +function crypted_resource_handler(metadata, response) { 1.19 + let obj = {id: "resource", 1.20 + modified: cryptoWrap.modified, 1.21 + payload: JSON.stringify(cryptoWrap.payload)}; 1.22 + return httpd_basic_auth_handler(JSON.stringify(obj), metadata, response); 1.23 +} 1.24 + 1.25 +function prepareCryptoWrap(collection, id) { 1.26 + let w = new CryptoWrapper(); 1.27 + w.cleartext.stuff = "my payload here"; 1.28 + w.collection = collection; 1.29 + w.id = id; 1.30 + return w; 1.31 +} 1.32 + 1.33 +function run_test() { 1.34 + let server; 1.35 + do_test_pending(); 1.36 + 1.37 + ensureLegacyIdentityManager(); 1.38 + Service.identity.username = "john@example.com"; 1.39 + Service.identity.syncKey = "a-abcde-abcde-abcde-abcde-abcde"; 1.40 + let keyBundle = Service.identity.syncKeyBundle; 1.41 + 1.42 + try { 1.43 + let log = Log.repository.getLogger("Test"); 1.44 + Log.repository.rootLogger.addAppender(new Log.DumpAppender()); 1.45 + 1.46 + log.info("Setting up server and authenticator"); 1.47 + 1.48 + server = httpd_setup({"/steam/resource": crypted_resource_handler}); 1.49 + 1.50 + log.info("Creating a record"); 1.51 + 1.52 + let cryptoUri = "http://localhost:8080/crypto/steam"; 1.53 + cryptoWrap = prepareCryptoWrap("steam", "resource"); 1.54 + 1.55 + log.info("cryptoWrap: " + cryptoWrap.toString()); 1.56 + 1.57 + log.info("Encrypting a record"); 1.58 + 1.59 + cryptoWrap.encrypt(keyBundle); 1.60 + log.info("Ciphertext is " + cryptoWrap.ciphertext); 1.61 + do_check_true(cryptoWrap.ciphertext != null); 1.62 + 1.63 + let firstIV = cryptoWrap.IV; 1.64 + 1.65 + log.info("Decrypting the record"); 1.66 + 1.67 + let payload = cryptoWrap.decrypt(keyBundle); 1.68 + do_check_eq(payload.stuff, "my payload here"); 1.69 + do_check_neq(payload, cryptoWrap.payload); // wrap.data.payload is the encrypted one 1.70 + 1.71 + log.info("Make sure multiple decrypts cause failures"); 1.72 + let error = ""; 1.73 + try { 1.74 + payload = cryptoWrap.decrypt(keyBundle); 1.75 + } 1.76 + catch(ex) { 1.77 + error = ex; 1.78 + } 1.79 + do_check_eq(error, "No ciphertext: nothing to decrypt?"); 1.80 + 1.81 + log.info("Re-encrypting the record with alternate payload"); 1.82 + 1.83 + cryptoWrap.cleartext.stuff = "another payload"; 1.84 + cryptoWrap.encrypt(keyBundle); 1.85 + let secondIV = cryptoWrap.IV; 1.86 + payload = cryptoWrap.decrypt(keyBundle); 1.87 + do_check_eq(payload.stuff, "another payload"); 1.88 + 1.89 + log.info("Make sure multiple encrypts use different IVs"); 1.90 + do_check_neq(firstIV, secondIV); 1.91 + 1.92 + log.info("Make sure differing ids cause failures"); 1.93 + cryptoWrap.encrypt(keyBundle); 1.94 + cryptoWrap.data.id = "other"; 1.95 + error = ""; 1.96 + try { 1.97 + cryptoWrap.decrypt(keyBundle); 1.98 + } 1.99 + catch(ex) { 1.100 + error = ex; 1.101 + } 1.102 + do_check_eq(error, "Record id mismatch: resource != other"); 1.103 + 1.104 + log.info("Make sure wrong hmacs cause failures"); 1.105 + cryptoWrap.encrypt(keyBundle); 1.106 + cryptoWrap.hmac = "foo"; 1.107 + error = ""; 1.108 + try { 1.109 + cryptoWrap.decrypt(keyBundle); 1.110 + } 1.111 + catch(ex) { 1.112 + error = ex; 1.113 + } 1.114 + do_check_eq(error.substr(0, 42), "Record SHA256 HMAC mismatch: should be foo"); 1.115 + 1.116 + // Checking per-collection keys and default key handling. 1.117 + 1.118 + generateNewKeys(Service.collectionKeys); 1.119 + let bu = "http://localhost:8080/storage/bookmarks/foo"; 1.120 + let bookmarkItem = prepareCryptoWrap("bookmarks", "foo"); 1.121 + bookmarkItem.encrypt(Service.collectionKeys.keyForCollection("bookmarks")); 1.122 + log.info("Ciphertext is " + bookmarkItem.ciphertext); 1.123 + do_check_true(bookmarkItem.ciphertext != null); 1.124 + log.info("Decrypting the record explicitly with the default key."); 1.125 + do_check_eq(bookmarkItem.decrypt(Service.collectionKeys._default).stuff, "my payload here"); 1.126 + 1.127 + // Per-collection keys. 1.128 + // Generate a key for "bookmarks". 1.129 + generateNewKeys(Service.collectionKeys, ["bookmarks"]); 1.130 + bookmarkItem = prepareCryptoWrap("bookmarks", "foo"); 1.131 + do_check_eq(bookmarkItem.collection, "bookmarks"); 1.132 + 1.133 + // Encrypt. This'll use the "bookmarks" encryption key, because we have a 1.134 + // special key for it. The same key will need to be used for decryption. 1.135 + bookmarkItem.encrypt(Service.collectionKeys.keyForCollection("bookmarks")); 1.136 + do_check_true(bookmarkItem.ciphertext != null); 1.137 + 1.138 + // Attempt to use the default key, because this is a collision that could 1.139 + // conceivably occur in the real world. Decryption will error, because 1.140 + // it's not the bookmarks key. 1.141 + let err; 1.142 + try { 1.143 + bookmarkItem.decrypt(Service.collectionKeys._default); 1.144 + } catch (ex) { 1.145 + err = ex; 1.146 + } 1.147 + do_check_eq("Record SHA256 HMAC mismatch", err.substr(0, 27)); 1.148 + 1.149 + // Explicitly check that it's using the bookmarks key. 1.150 + // This should succeed. 1.151 + do_check_eq(bookmarkItem.decrypt(Service.collectionKeys.keyForCollection("bookmarks")).stuff, 1.152 + "my payload here"); 1.153 + 1.154 + log.info("Done!"); 1.155 + } 1.156 + finally { 1.157 + server.stop(do_test_finished); 1.158 + } 1.159 +}