home *** CD-ROM | disk | FTP | other *** search
/ Minami 83 / MINAMI83.iso / Extra / DivXInstaller.exe / $PLUGINSDIR / GoogleToolbarFirefox.msi / xpi / amulet-jslib / url-crypto.js < prev    next >
Text File  |  2006-08-07  |  14KB  |  415 lines

  1. function PROT_UrlCrypto() {
  2. this.debugZone = "urlcrypto";
  3. this.hasher_ = new G_CryptoHasher();
  4. this.base64_ = new G_Base64();
  5. this.rc4_ = new ARC4();
  6. this.VERSION = PROT_UrlCrypto.VERSION;
  7. this.RC4_DISCARD_BYTES = PROT_UrlCrypto.RC4_DISCARD_BYTES;
  8. this.VERSION_QUERY_PARAM_NAME = PROT_UrlCrypto.QPS.VERSION_QUERY_PARAM_NAME;
  9. this.ENCRYPTED_PARAMS_PARAM_NAME =
  10. PROT_UrlCrypto.QPS.ENCRYPTED_PARAMS_PARAM_NAME;
  11. this.COUNT_QUERY_PARAM_NAME = PROT_UrlCrypto.QPS.COUNT_QUERY_PARAM_NAME;
  12. this.WRAPPEDKEY_QUERY_PARAM_NAME =
  13. PROT_UrlCrypto.QPS.WRAPPEDKEY_QUERY_PARAM_NAME;
  14. this.macer_ = new G_CryptoHasher(); // don't use hasher_
  15. this.macInitialized_ = false;
  16. this.separator_ = ":coolgoog:";
  17. this.separatorArray_ = this.base64_.arrayifyString(this.separator_);
  18. }
  19. PROT_UrlCrypto.VERSION = "1";
  20. PROT_UrlCrypto.RC4_DISCARD_BYTES = 1600;
  21. PROT_UrlCrypto.QPS = {};
  22. PROT_UrlCrypto.QPS.VERSION_QUERY_PARAM_NAME = "encver";
  23. PROT_UrlCrypto.QPS.ENCRYPTED_PARAMS_PARAM_NAME = "encparams";
  24. PROT_UrlCrypto.QPS.COUNT_QUERY_PARAM_NAME = "nonce";
  25. PROT_UrlCrypto.QPS.WRAPPEDKEY_QUERY_PARAM_NAME = "wrkey";
  26. PROT_UrlCrypto.prototype.getManager = function() {
  27. return this.manager_;
  28. }
  29. PROT_UrlCrypto.prototype.appendParams_ = function(params) {
  30. var queryString = "";
  31. for (var param in params)
  32. queryString += "&" + param + "=" + encodeURIComponent(params[param]);
  33. return queryString;
  34. }
  35. PROT_UrlCrypto.prototype.maybeCryptParams = function(params) {
  36. if (!this.manager_)
  37. throw new Error("Need a key manager for UrlCrypto");
  38. if (typeof params != "object")
  39. throw new Error("params is an associative array of name/value params");
  40. var clientKeyArray = this.manager_.getClientKeyArray();
  41. var wrappedKey = this.manager_.getWrappedKey();
  42. if (!clientKeyArray || !wrappedKey) {
  43. G_Debug(this, "No key; can't encrypt query params");
  44. return params;
  45. }
  46. var queryString = this.appendParams_(params);
  47. var counter = this.getCount_();
  48. counter = counter & 0xFFFFFFFF;
  49. var encrypted = this.encryptV1(clientKeyArray,
  50. this.VERSION,
  51. counter,
  52. this.base64_.arrayifyString(queryString));
  53. params = {};
  54. params[this.VERSION_QUERY_PARAM_NAME] = this.VERSION;
  55. params[this.COUNT_QUERY_PARAM_NAME] = counter;
  56. params[this.WRAPPEDKEY_QUERY_PARAM_NAME] = wrappedKey;
  57. params[this.ENCRYPTED_PARAMS_PARAM_NAME] = encrypted;
  58. return params;
  59. }
  60. PROT_UrlCrypto.prototype.maybeCryptParam = function(param, maxLen) {
  61. if (!this.manager_)
  62. throw new Error("Need a key manager for UrlCrypto");
  63. if (maxLen < 1)
  64. throw new Error("maxLen must be at least 1");
  65. var clientKeyArray = this.manager_.getClientKeyArray();
  66. var wrappedKey = this.manager_.getWrappedKey();
  67. if (!clientKeyArray || !wrappedKey) {
  68. G_Debug(this, "No key; can't encrypt param");
  69. return param.substring(0, maxLen);
  70. }
  71. var counter = this.getCount_();
  72. counter = counter & 0xFFFFFFFF;
  73. var outParams = this.VERSION_QUERY_PARAM_NAME + "="
  74. + encodeURIComponent(this.VERSION) + "&"
  75. + this.COUNT_QUERY_PARAM_NAME + "="
  76. + encodeURIComponent(counter) + "&"
  77. + this.WRAPPEDKEY_QUERY_PARAM_NAME + "="
  78. + encodeURIComponent(wrappedKey) + "&"
  79. + this.ENCRYPTED_PARAMS_PARAM_NAME + "=";
  80. var remLength = (maxLen - outParams.length) * 3 / 4 - 1;
  81. if (remLength < 0) {
  82. throw new Error("excrypted url too long");
  83. }
  84. param = param.substring(0, remLength);
  85. if ('%' == param[remLength - 1]) {
  86. param = param.substring(0, remLength - 1);
  87. } else if ('%' == param[remLength - 2]) {
  88. param = param.substring(0, remLength - 2);
  89. }
  90. var encrypted = this.encryptV1(clientKeyArray,
  91. this.VERSION,
  92. counter,
  93. this.base64_.arrayifyString(param));
  94. return outParams + encrypted;
  95. }
  96. PROT_UrlCrypto.prototype.encryptV1 = function(clientKeyArray,
  97. version,
  98. counter,
  99. inOutArray) {
  100. if (version != "1")
  101. throw new Error("Unknown encryption version");
  102. var key = this.deriveEncryptionKey(clientKeyArray, counter);
  103. this.rc4_.setKey(key, key.length);
  104. if (this.RC4_DISCARD_BYTES > 0)
  105. this.rc4_.discard(this.RC4_DISCARD_BYTES);
  106. this.rc4_.crypt(inOutArray, inOutArray.length);
  107. return this.base64_.encodeByteArray(inOutArray, true /* websafe */);
  108. }
  109. PROT_UrlCrypto.prototype.deriveEncryptionKey = function(clientKeyArray,
  110. count) {
  111. G_Assert(this, clientKeyArray instanceof Array,
  112. "Client key should be an array of bytes");
  113. G_Assert(this, typeof count == "number", "Count should be a number");
  114. var paddingArray = [];
  115. paddingArray.push(count >> 24);
  116. paddingArray.push((count >> 16) & 0xFF);
  117. paddingArray.push((count >> 8) & 0xFF);
  118. paddingArray.push(count & 0xFF);
  119. this.hasher_.init(G_CryptoHasher.algorithms.MD5);
  120. this.hasher_.updateFromArray(clientKeyArray);
  121. this.hasher_.updateFromArray(paddingArray);
  122. return this.base64_.arrayifyString(this.hasher_.digestRaw());
  123. }
  124. PROT_UrlCrypto.prototype.getCount_ = function() {
  125. return ((new Date).getTime() & 0xFFFFFFFF);
  126. }
  127. PROT_UrlCrypto.prototype.initMac = function(opt_clientKeyArray) {
  128. if (this.macInitialized_) {
  129. throw new Error("Can't interleave calls to initMac.  Please use another " +
  130. "UrlCrypto object.");
  131. }
  132. this.macInitialized_ = true;
  133. var clientKeyArray = null;
  134. if (!!opt_clientKeyArray) {
  135. clientKeyArray = opt_clientKeyArray;
  136. } else {
  137. clientKeyArray = this.manager_.getClientKeyArray();
  138. }
  139. this.macer_.init(G_CryptoHasher.algorithms.MD5);
  140. this.macer_.updateFromArray(clientKeyArray);
  141. this.macer_.updateFromArray(this.separatorArray_);
  142. }
  143. PROT_UrlCrypto.prototype.updateMacFromString = function(s) {
  144. if (!this.macInitialized_) {
  145. throw new Error ("Initialize mac first");
  146. }
  147. var stream = Cc['@mozilla.org/io/string-input-stream;1']
  148. .createInstance(Ci.nsIStringInputStream);
  149. stream.setData(s, s.length);
  150. if (stream.available() > 0) {
  151. this.macer_.updateFromStream(stream);
  152. }
  153. }
  154. PROT_UrlCrypto.prototype.finishMac = function(opt_clientKeyArray) {
  155. var clientKeyArray = null;
  156. if (!!opt_clientKeyArray) {
  157. clientKeyArray = opt_clientKeyArray;
  158. } else {
  159. clientKeyArray = this.manager_.getClientKeyArray();
  160. }
  161. this.macer_.updateFromArray(this.separatorArray_);
  162. this.macer_.updateFromArray(clientKeyArray);
  163. this.macInitialized_ = false;
  164. return this.macer_.digestBase64();
  165. }
  166. PROT_UrlCrypto.prototype.computeMac = function(data,
  167. opt_outputRaw,
  168. opt_clientKeyArray,
  169. opt_separatorArray) {
  170. var clientKeyArray = null;
  171. var separatorArray = null;
  172. if (!!opt_clientKeyArray) {
  173. clientKeyArray = opt_clientKeyArray;
  174. } else {
  175. clientKeyArray = this.manager_.getClientKeyArray();
  176. }
  177. if (!!opt_separatorArray) {
  178. separatorArray = opt_separatorArray;
  179. } else {
  180. separatorArray = this.separatorArray_;
  181. }
  182. this.macer_.init(G_CryptoHasher.algorithms.MD5);
  183. this.macer_.updateFromArray(clientKeyArray);
  184. this.macer_.updateFromArray(separatorArray);
  185. var stream = Cc['@mozilla.org/io/string-input-stream;1']
  186. .createInstance(Ci.nsIStringInputStream);
  187. stream.setData(data, data.length);
  188. if (stream.available() > 0) {
  189. this.macer_.updateFromStream(stream);
  190. }
  191. this.macer_.updateFromArray(separatorArray);
  192. this.macer_.updateFromArray(clientKeyArray);
  193. if (!!opt_outputRaw) {
  194. return this.macer_.digestRaw();
  195. }
  196. return this.macer_.digestBase64();
  197. }
  198. function TEST_PROT_UrlCrypto() {
  199. if (G_GDEBUG) {
  200. var z = "urlcrypto UNITTEST";
  201. G_debugService.enableZone(z);
  202. G_Debug(z, "Starting");
  203. var kf = "test.txt";
  204. function removeTestFile(f) {
  205. var appDir = new PROT_ApplicationDirectory();
  206. var file = appDir.getAppDirFileInterface();
  207. file.append(f);
  208. if (file.exists())
  209. file.remove(false /* do not recurse */ );
  210. };
  211. removeTestFile(kf);
  212. var km = new PROT_UrlCryptoKeyManager(kf, true /* testing */);
  213. var c = new PROT_UrlCrypto();
  214. var fakeManager = {
  215. getClientKeyArray: function() { return null; },
  216. getWrappedKey: function() { return null; },
  217. };
  218. c.manager_ = fakeManager;
  219. var params = {
  220. foo: "bar",
  221. baz: "bomb",
  222. };
  223. G_Assert(z, c.maybeCryptParams(params)["foo"] === "bar",
  224. "How can we encrypt if we don't have a key?");
  225. c.manager_ = km;
  226. G_Assert(z, c.maybeCryptParams(params)["foo"] === "bar",
  227. "Again, how can we encrypt if we don't have a key?");
  228. var realResponse = "clientkey:24:dtmbEN1kgN/LmuEoYifaFw==\n" +
  229. "wrappedkey:24:MTpPH3pnLDKihecOci+0W5dk";
  230. km.onGetKeyResponse(realResponse);
  231. var crypted = c.maybeCryptParams(params);
  232. G_Assert(z, crypted["foo"] === undefined, "We have a key but can't crypt");
  233. G_Assert(z, crypted["bomb"] === undefined, "We have a key but can't crypt");
  234. for (var p in PROT_UrlCrypto.QPS)
  235. G_Assert(z, crypted[PROT_UrlCrypto.QPS[p]] != undefined,
  236. "Output query params doesn't have: " + PROT_UrlCrypto.QPS[p]);
  237. var b64 = new G_Base64();
  238. function arrayEquals(a1, a2) {
  239. if (a1.length != a2.length)
  240. return false;
  241. for (var i = 0; i < a1.length; i++)
  242. if (typeof a1[i] != typeof a2[i] || a1[i] != a2[i])
  243. return false;
  244. return true;
  245. };
  246. function arrayAsString(a) {
  247. var s = "[";
  248. for (var i = 0; i < a.length; i++)
  249. s += a[i] + ",";
  250. return s + "]";
  251. };
  252. function printArray(a) {
  253. var s = arrayAsString(a);
  254. G_Debug(z, s);
  255. };
  256. var keySizeBytes = km.clientKeyArray_.length;
  257. var startCrypt = (new Date).getTime();
  258. var numCrypts = 0;
  259. var doLongTest = false;
  260. if (doLongTest) {
  261. for (var i = 0; i < 2 * keySizeBytes; i++) {
  262. var clientKeyArray = [];
  263. for (var j = 0; j < keySizeBytes; j++)
  264. clientKeyArray[j] = i + j;
  265. for (var count = 0; count < 40; count++) {
  266. var payload = "";
  267. for (var payloadPadding = 0; payloadPadding < count; payloadPadding++)
  268. payload += "a";
  269. var plaintext1 = b64.arrayifyString(payload);
  270. var plaintext2 = b64.arrayifyString(payload);
  271. var plaintext3 = b64.arrayifyString(payload);
  272. numCrypts++;
  273. var ciphertext1 = c.encryptV1(clientKeyArray,
  274. "1",
  275. count,
  276. plaintext1);
  277. numCrypts++;
  278. var ciphertext2 = c.encryptV1(clientKeyArray,
  279. "1",
  280. count,
  281. plaintext2);
  282. G_Assert(z, ciphertext1 === ciphertext2,
  283. "Two plaintexts having different ciphertexts:" +
  284. ciphertext1 + " " + ciphertext2);
  285. numCrypts++;
  286. var ciphertext3 = c.encryptV1(clientKeyArray,
  287. "1",
  288. count,
  289. b64.decodeString(ciphertext2),
  290. true /* websafe */);
  291. G_Assert(z, arrayEquals(plaintext3, b64.decodeString(ciphertext3,
  292. true/*websafe*/)),
  293. "Encryption and decryption not symmetrical");
  294. }
  295. }
  296. var endCrypt = (new Date).getTime();
  297. var totalMS = endCrypt - startCrypt;
  298. G_Debug(z, "Info: Did " + numCrypts + " encryptions in " +
  299. totalMS + "ms, for an average of " +
  300. (totalMS / numCrypts) + "ms per crypt()");
  301. }
  302. var ciphertexts = {};
  303. ciphertexts[0]="";
  304. ciphertexts[1]="dA==";
  305. ciphertexts[2]="akY=";
  306. ciphertexts[3]="u5mV";
  307. ciphertexts[4]="bhtioQ==";
  308. ciphertexts[5]="m2wSZnQ=";
  309. ciphertexts[6]="zd6gWyDO";
  310. ciphertexts[7]="bBN0WVrlCg==";
  311. ciphertexts[8]="Z6U_6bMelFM=";
  312. ciphertexts[9]="UVoiytL-gHzp";
  313. ciphertexts[10]="3Xr_ZMmdmvg7zw==";
  314. ciphertexts[11]="PIIyif7NFRS57mY=";
  315. ciphertexts[12]="QEKXrRWdZ3poJVSp";
  316. ciphertexts[13]="T3zsAsooHuAnflNsNQ==";
  317. ciphertexts[14]="qgYtOJjZSIByo0KtOG0=";
  318. ciphertexts[15]="NsEGHGK6Ju6FjD59Byai";
  319. ciphertexts[16]="1RVIsC0HYoUEycoA_0UL2w==";
  320. ciphertexts[17]="0xXe6Lsb1tZ79T96AJTT-ps=";
  321. ciphertexts[18]="cVXQCYoA4RV8t1CODXuCS88y";
  322. ciphertexts[19]="hVf4pd4WP4wPwSyqEXRRkQZSQA==";
  323. ciphertexts[20]="F6Y9MHwhd1e-bDHhqNSonZbR2Sg=";
  324. ciphertexts[21]="TiMClYbLUdyYweW8IDytU_HD2wTM";
  325. ciphertexts[22]="tYQtNqz83KXE4eqn6GhAu6ZZ23SqYw==";
  326. ciphertexts[23]="qjL-dMpiQ2LYgkYT5IfmE1FlN36wHek=";
  327. ciphertexts[24]="cL7HHiOZ9PbkvZ9yrJLiv4HXcw4Nf7y7";
  328. ciphertexts[25]="k4I-fdR6CyzxOpR_QEG5rnvPB8IbmRnpFg==";
  329. ciphertexts[26]="7LjCfA1dCMjAVT_O8DpiTQ0G7igwQ1HTUMU=";
  330. ciphertexts[27]="CAtijc6nB-REwAkqimToMn8RC_eZAaJy9Gn4";
  331. ciphertexts[28]="z8sEB1lDI32wsOkgYbVZ5pxIbpCrha9BmcqxFQ==";
  332. ciphertexts[29]="2eysfzsfGav0vPRsSnFl8H8fg9dQCT_bSiZwno0=";
  333. ciphertexts[30]="2BBNlF_mtV9TB2jZHHqCAtzkJQFdVKFn7N8YxsI9";
  334. ciphertexts[31]="9h4-nldHAr77Boks7lPzsi8TwVCIQzSkiJp2xatbGg==";
  335. ciphertexts[32]="DHTB8bDTXpUIrZ2ZlAujXLi-501NoWUVIEQJLaKCpqQ=";
  336. ciphertexts[33]="E9Av2GgnZg_q5r-JLSzM_ShCu1yPF2VeCaQfPPXSSE4I";
  337. ciphertexts[34]="UJzEucVBnGEfRNBQ6tvbaro0_I_-mQeJMpU2zQnfFdBuFg==";
  338. ciphertexts[35]="_p0OYras-Vn2rQ9X-J0dFRnhCfytuTEjheUTU7Ueaf1rIA4=";
  339. ciphertexts[36]="Q0nZXFPJbpx1WZPP-lLPuSGR-pD08B4CAW-6Uf0eEkS05-oM";
  340. ciphertexts[37]="XeKfieZGc9bPh7nRtCgujF8OY14zbIZSK20Lwg1HTpHi9HfXVQ==";
  341. var clientKeyArray = b64.decodeString("dtmbEN1kgN/LmuEoYifaFw==");
  342. var count = 0xFEDCBA09;
  343. var plaintext = "http://www.foobar.com/this?is&some=url";
  344. for (var i = 0; i < plaintext.length; i++) {
  345. var plaintextArray = b64.arrayifyString(plaintext.substring(0, i));
  346. var crypted = c.encryptV1(clientKeyArray,
  347. "1",
  348. count + i,
  349. plaintextArray);
  350. G_Assert(z, crypted === ciphertexts[i],
  351. "Generated unexpected ciphertext");
  352. }
  353. var md5texts = [ "",
  354. "a",
  355. "abc",
  356. "message digest",
  357. "abcdefghijklmnopqrstuvwxyz",
  358. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
  359. "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
  360. ];
  361. var md5digests = [ "d41d8cd98f00b204e9800998ecf8427e",
  362. "0cc175b9c0f1b6a831c399e269772661",
  363. "900150983cd24fb0d6963f7d28e17f72",
  364. "f96b697d7cb7938d525a2f31aaf161d0",
  365. "c3fcd3d76192e4007dfb496cca67e13b",
  366. "d174ab98d277d9f5a5611c2c9f419d9f",
  367. "57edf4a22be3c955ac49da2e2107b67a"
  368. ];
  369. var expected_mac = [];
  370. expected_mac[0] = "ZOJ6Mk+ccC6R7BwseqCYQQ==";
  371. expected_mac[1] = "zWM7tvcsuH/MSEviNiRbOA==";
  372. expected_mac[2] = "ZAUVyls/6ZVN3Np8v3pX3g==";
  373. expected_mac[3] = "Zq6gF7RkPwKqlicuxrO4mg==";
  374. expected_mac[4] = "/LOJETSnqSW3q4u1hs/0Pg==";
  375. expected_mac[5] = "jjOEX7H2uchOznxIGuqzJg==";
  376. expected_mac[6] = "Tje7aP/Rk/gkSH4he0KMQQ==";
  377. var hasher = new G_CryptoHasher();
  378. for (var i = 0; i < md5texts.length; i++) {
  379. var computedMac = c.computeMac(md5texts[i], true /* output raw */, [], []);
  380. var hex = hasher.toHex_(computedMac).toLowerCase();
  381. G_Assert(z, hex == md5digests[i],
  382. "MD5(" + md5texts[i] + ") = " + md5digests[i] + ", not " + hex);
  383. }
  384. for (var i = 0; i < md5texts.length; i++) {
  385. var computedMac = c.computeMac(md5texts[i],
  386. false /* output Base64 */,
  387. clientKeyArray);
  388. G_Assert(z, computedMac == expected_mac[i],
  389. "Wrong mac generated for " + md5texts[i]);
  390. }
  391. var wholeString = md5texts[0] + md5texts[1];
  392. var wholeStringMac = c.computeMac(wholeString,
  393. false /* output Base64 */,
  394. clientKeyArray);
  395. expected_mac = "zWM7tvcsuH/MSEviNiRbOA==";
  396. c.initMac(clientKeyArray);
  397. c.updateMacFromString(md5texts[0]);
  398. c.updateMacFromString(md5texts[1]);
  399. var piecemealMac = c.finishMac(clientKeyArray);
  400. G_Assert(z, piecemealMac == wholeStringMac,
  401. "Computed different values for mac when adding line by line!");
  402. G_Assert(z, piecemealMac == expected_mac, "Didn't generate expected mac");
  403. expected_mac = "iA5vLUidpXAPwfcAH9+8OQ==";
  404. var set3data = "";
  405. for (var i = 1; i <= 3; i++) {
  406. set3data += "+white" + i + ".com\t1\n";
  407. }
  408. var computedMac = c.computeMac(set3data, false /*Base64*/, clientKeyArray);
  409. G_Assert(z, expected_mac == computedMac,
  410. "Expected " + expected_mac + " got " + computedMac);
  411. removeTestFile(kf);
  412. G_Debug(z, "PASS");
  413. }
  414. }
  415.