From 587663f8665101d9402a45643a6680979e363316 Mon Sep 17 00:00:00 2001 From: "tony@ponderer.org" Date: Mon, 4 Jun 2007 22:22:00 -0700 Subject: [PATCH] =?UTF-8?q?Bug=20381379=20-=20remove=20G=5FBase64original?= =?UTF-8?q?=20patch=20by=20Simon=20B=C3=BCnzli,=20r=3Dme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../safebrowsing/content/application.js | 1 - .../content/enchash-decrypter.js | 7 +- .../url-classifier/content/moz/base64.js | 295 ------------------ .../content/moz/cryptohasher.js | 14 +- .../content/url-crypto-key-manager.js | 4 +- .../url-classifier/content/url-crypto.js | 46 +-- .../url-classifier/src/nsUrlClassifierLib.js | 1 - .../src/nsUrlClassifierTable.js | 1 - 8 files changed, 39 insertions(+), 330 deletions(-) delete mode 100644 toolkit/components/url-classifier/content/moz/base64.js diff --git a/browser/components/safebrowsing/content/application.js b/browser/components/safebrowsing/content/application.js index 9c8b21fd356..c2c15afb7f3 100644 --- a/browser/components/safebrowsing/content/application.js +++ b/browser/components/safebrowsing/content/application.js @@ -61,7 +61,6 @@ function PROT_Application() { G_DebugL("UNITTESTS", "STARTING UNITTESTS"); TEST_G_Protocol4Parser(); - TEST_G_Base64(); TEST_G_CryptoHasher(); TEST_PROT_EnchashDecrypter(); TEST_PROT_TRTable(); diff --git a/toolkit/components/url-classifier/content/enchash-decrypter.js b/toolkit/components/url-classifier/content/enchash-decrypter.js index c5643e5299f..2ff8f3b832b 100644 --- a/toolkit/components/url-classifier/content/enchash-decrypter.js +++ b/toolkit/components/url-classifier/content/enchash-decrypter.js @@ -61,7 +61,6 @@ function PROT_EnchashDecrypter() { this.debugZone = "enchashdecrypter"; this.REs_ = PROT_EnchashDecrypter.REs; this.hasher_ = new G_CryptoHasher(); - this.base64_ = new G_Base64(); this.streamCipher_ = Cc["@mozilla.org/security/streamcipher;1"] .createInstance(Ci.nsIStreamCipher); } @@ -301,7 +300,7 @@ PROT_EnchashDecrypter.prototype.canonicalNum_ = function(num, bytes, octal) { PROT_EnchashDecrypter.prototype.getLookupKey = function(host) { var dataKey = PROT_EnchashDecrypter.DATABASE_SALT + host; - dataKey = this.base64_.arrayifyString(dataKey); + dataKey = Array.map(dataKey, function(c) { return c.charCodeAt(0); }); this.hasher_.init(G_CryptoHasher.algorithms.MD5); var lookupDigest = this.hasher_.updateFromArray(dataKey); @@ -311,9 +310,7 @@ PROT_EnchashDecrypter.prototype.getLookupKey = function(host) { } PROT_EnchashDecrypter.prototype.decryptData = function(data, host) { - // XXX: base 64 decoding should be done in C++ - var asciiArray = this.base64_.decodeString(data); - var ascii = this.base64_.stringifyArray(asciiArray); + var ascii = atob(data); var random_salt = ascii.slice(0, PROT_EnchashDecrypter.SALT_LENGTH); var encrypted_data = ascii.slice(PROT_EnchashDecrypter.SALT_LENGTH); diff --git a/toolkit/components/url-classifier/content/moz/base64.js b/toolkit/components/url-classifier/content/moz/base64.js deleted file mode 100644 index e8fcd9b6942..00000000000 --- a/toolkit/components/url-classifier/content/moz/base64.js +++ /dev/null @@ -1,295 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Google Safe Browsing. -# -# The Initial Developer of the Original Code is Google Inc. -# Portions created by the Initial Developer are Copyright (C) 2006 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Fritz Schneider (original author) -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - - -// Base64 en/decoding. Not much to say here except that we work with -// decoded values in arrays of bytes. By "byte" I mean a number in [0, -// 255]. - - -/** - * Base64 en/decoder. Useful in contexts that don't have atob/btoa, or - * when you need a custom encoding function (e.g., websafe base64). - * - * @constructor - */ -function G_Base64() { - this.byteToCharMap_ = {}; - this.charToByteMap_ = {}; - this.byteToCharMapWebSafe_ = {}; - this.charToByteMapWebSafe_ = {}; - this.init_(); -} - -/** - * Our default alphabet. Value 64 (=) is special; it means "nothing." - */ -G_Base64.ENCODED_VALS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz" + - "0123456789+/="; - -/** - * Our websafe alphabet. Value 64 (=) is special; it means "nothing." - */ -G_Base64.ENCODED_VALS_WEBSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + - "abcdefghijklmnopqrstuvwxyz" + - "0123456789-_="; - -/** - * We want quick mappings back and forth, so we precompute two maps. - */ -G_Base64.prototype.init_ = function() { - for (var i = 0; i < G_Base64.ENCODED_VALS.length; i++) { - this.byteToCharMap_[i] = G_Base64.ENCODED_VALS.charAt(i); - this.charToByteMap_[this.byteToCharMap_[i]] = i; - this.byteToCharMapWebSafe_[i] = G_Base64.ENCODED_VALS_WEBSAFE.charAt(i); - this.charToByteMapWebSafe_[this.byteToCharMapWebSafe_[i]] = i; - } -} - -/** - * Base64-encode an array of bytes. - * - * @param input An array of bytes (numbers with value in [0, 255]) to encode - * - * @param opt_webSafe Boolean indicating we should use the alternative alphabet - * - * @returns String containing the base64 encoding - */ -G_Base64.prototype.encodeByteArray = function(input, opt_webSafe) { - - if (!(input instanceof Array)) - throw new Error("encodeByteArray takes an array as a parameter"); - - var byteToCharMap = opt_webSafe ? - this.byteToCharMapWebSafe_ : - this.byteToCharMap_; - - var output = []; - - var i = 0; - while (i < input.length) { - - var byte1 = input[i]; - var haveByte2 = i + 1 < input.length; - var byte2 = haveByte2 ? input[i + 1] : 0; - var haveByte3 = i + 2 < input.length; - var byte3 = haveByte3 ? input[i + 2] : 0; - - var outByte1 = byte1 >> 2; - var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4); - var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6); - var outByte4 = byte3 & 0x3F; - - if (!haveByte3) { - outByte4 = 64; - - if (!haveByte2) - outByte3 = 64; - } - - output.push(byteToCharMap[outByte1]); - output.push(byteToCharMap[outByte2]); - output.push(byteToCharMap[outByte3]); - output.push(byteToCharMap[outByte4]); - - i += 3; - } - - return output.join(""); -} - -/** - * Base64-decode a string. - * - * @param input String to decode - * - * @param opt_webSafe Boolean indicating we should use the alternative alphabet - * - * @returns Array of bytes representing the decoded value. - */ -G_Base64.prototype.decodeString = function(input, opt_webSafe) { - - if (input.length % 4) - throw new Error("Length of b64-encoded data must be zero mod four"); - - var charToByteMap = opt_webSafe ? - this.charToByteMapWebSafe_ : - this.charToByteMap_; - - var output = []; - - var i = 0; - while (i < input.length) { - - var byte1 = charToByteMap[input.charAt(i)]; - var byte2 = charToByteMap[input.charAt(i + 1)]; - var byte3 = charToByteMap[input.charAt(i + 2)]; - var byte4 = charToByteMap[input.charAt(i + 3)]; - - if (byte1 === undefined || byte2 === undefined || - byte3 === undefined || byte4 === undefined) - throw new Error("String contains characters not in our alphabet: " + - input); - - var outByte1 = (byte1 << 2) | (byte2 >> 4); - output.push(outByte1); - - if (byte3 != 64) { - var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2); - output.push(outByte2); - - if (byte4 != 64) { - var outByte3 = ((byte3 << 6) & 0xC0) | byte4; - output.push(outByte3); - } - } - - i += 4; - } - - return output; -} - -/** - * Helper function that turns a string into an array of numbers. - * - * @param str String to arrify - * - * @returns Array holding numbers corresponding to the UCS character codes - * of each character in str - */ -G_Base64.prototype.arrayifyString = function(str) { - var output = []; - for (var i = 0; i < str.length; i++) - output.push(str.charCodeAt(i)); - return output; -} - -/** - * Helper function that turns an array of numbers into the string - * given by the concatenation of the characters to which the numbesr - * correspond (got that?). - * - * @param array Array of numbers representing characters - * - * @returns Stringification of the array - */ -G_Base64.prototype.stringifyArray = function(array) { - var output = []; - for (var i = 0; i < array.length; i++) - output[i] = String.fromCharCode(array[i]); - return output.join(""); -} - - -#ifdef DEBUG -/** - * Lame unittesting function - */ -function TEST_G_Base64() { - if (G_GDEBUG) { - var z = "base64 UNITTEST"; - G_debugService.enableZone(z); - G_Debug(z, "Starting"); - - var b = new G_Base64(); - - // Let's see if it's sane by feeding it some well-known values. Index i - // has the input and index i+1 has the expected value. - - var tests = - [ "", "", - "f", "Zg==", - "fo", "Zm8=", - "foo", "Zm9v", - "foob", "Zm9vYg==", - "fooba", "Zm9vYmE=", - "foobar", "Zm9vYmFy"]; - - for (var i = 0; i < tests.length; i += 2) { - var enc = b.encodeByteArray(b.arrayifyString(tests[i])); - G_Assert(z, enc === tests[i + 1], - "Error encoding: " + tests[i] + " (got " + enc + - " but wanted " + tests[i + 1] + ")"); - var dec = b.stringifyArray(b.decodeString(enc)); - G_Assert(z, dec === tests[i], - "Error deocding " + enc + " (got " + dec + - " but wanted " + tests[i] + ")"); - } - - // Now run it through its paces - - var numIterations = 100; - for (var i = 0; i < numIterations; i++) { - - var input = []; - for (var j = 0; j < i; j++) - input[j] = j % 256; - - var encoded = b.encodeByteArray(input); - var decoded = b.decodeString(encoded); - G_Assert(z, !(encoded.length % 4), "Encoded length not a multiple of 4?"); - G_Assert(z, input.length == decoded.length, - "Decoded length not equal to input length?"); - - for (var j = 0; j < i; j++) - G_Assert(z, input[j] === decoded[j], "Values differ at position " + j); - } - - // Test non-websafe / websafe difference - var test = ">>>???>>>???"; - var enc = b.encodeByteArray(b.arrayifyString(test)); - G_Assert(z, enc == "Pj4+Pz8/Pj4+Pz8/", "Non-websafe broken?"); - enc = b.encodeByteArray(b.arrayifyString(test), true /* websafe */); - G_Assert(z, enc == "Pj4-Pz8_Pj4-Pz8_", "Websafe encoding broken"); - var dec = b.stringifyArray(b.decodeString(enc, true /* websafe */)); - G_Assert(z, dec === test, "Websafe dencoding broken"); - - // Test parsing malformed characters - var caught = false; - try { - b.decodeString("foooooo+oooo", true /*websafe*/); - } catch(e) { - caught = true; - } - G_Assert(z, caught, "Didn't throw on malformed input"); - - G_Debug(z, "PASSED"); - - } -} -#endif diff --git a/toolkit/components/url-classifier/content/moz/cryptohasher.js b/toolkit/components/url-classifier/content/moz/cryptohasher.js index e36afd3a521..692c09262e0 100644 --- a/toolkit/components/url-classifier/content/moz/cryptohasher.js +++ b/toolkit/components/url-classifier/content/moz/cryptohasher.js @@ -54,7 +54,6 @@ */ function G_CryptoHasher() { this.debugZone = "cryptohasher"; - this.decoder_ = new G_Base64(); this.hasher_ = null; } @@ -90,10 +89,7 @@ G_CryptoHasher.prototype.init = function(algorithm) { /** * Update the hash's internal state with input given in a string. Can be - * called multiple times for incrementeal hash updates. Note that this function - * is slllloooowww since it uses the a javascript implementation to convert the - * string to an array. If you need something faster, use updateFromStream() with - * an XPCOM stream. + * called multiple times for incrementeal hash updates. * * @param input String containing data to hash. */ @@ -101,7 +97,10 @@ G_CryptoHasher.prototype.updateFromString = function(input) { if (!this.hasher_) throw new Error("You must initialize the hasher first!"); - this.hasher_.update(this.decoder_.arrayifyString(input), input.length); + var stream = Cc['@mozilla.org/io/string-input-stream;1'] + .createInstance(Ci.nsIStringInputStream); + stream.setData(input, input.length); + this.updateFromStream(stream); } /** @@ -125,7 +124,8 @@ G_CryptoHasher.prototype.updateFromStream = function(stream) { if (!this.hasher_) throw new Error("You must initialize the hasher first!"); - this.hasher_.updateFromStream(stream, stream.available()); + if (stream.available()) + this.hasher_.updateFromStream(stream, stream.available()); } /** diff --git a/toolkit/components/url-classifier/content/url-crypto-key-manager.js b/toolkit/components/url-classifier/content/url-crypto-key-manager.js index efae254109b..2cceff4ad58 100644 --- a/toolkit/components/url-classifier/content/url-crypto-key-manager.js +++ b/toolkit/components/url-classifier/content/url-crypto-key-manager.js @@ -92,7 +92,6 @@ const kKeyFilename = "kf.txt"; function PROT_UrlCryptoKeyManager(opt_keyFilename, opt_testing) { this.debugZone = "urlcryptokeymanager"; this.testing_ = !!opt_testing; - this.base64_ = new G_Base64(); this.clientKey_ = null; // Base64-encoded, as fetched from server this.clientKeyArray_ = null; // Base64-decoded into an array of numbers this.wrappedKey_ = null; // Opaque websafe base64-encoded server key @@ -254,7 +253,8 @@ PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey, G_Debug(this, "Replacing " + this.clientKey_ + " with " + clientKey); this.clientKey_ = clientKey; - this.clientKeyArray_ = this.base64_.decodeString(this.clientKey_); + this.clientKeyArray_ = Array.map(atob(clientKey), + function(c) { return c.charCodeAt(0); }); this.wrappedKey_ = wrappedKey; this.serializeKey_(this.clientKey_, this.wrappedKey_); diff --git a/toolkit/components/url-classifier/content/url-crypto.js b/toolkit/components/url-classifier/content/url-crypto.js index bbc6acf0651..a82a9a73ecb 100644 --- a/toolkit/components/url-classifier/content/url-crypto.js +++ b/toolkit/components/url-classifier/content/url-crypto.js @@ -61,7 +61,6 @@ function PROT_UrlCrypto() { this.debugZone = "urlcrypto"; this.hasher_ = new G_CryptoHasher(); - this.base64_ = new G_Base64(); this.streamCipher_ = Cc["@mozilla.org/security/streamcipher;1"] .createInstance(Ci.nsIStreamCipher); @@ -88,7 +87,8 @@ function PROT_UrlCrypto() { this.macInitialized_ = false; // Separator to prevent leakage between key and data when computing mac this.separator_ = ":coolgoog:"; - this.separatorArray_ = this.base64_.arrayifyString(this.separator_); + this.separatorArray_ = Array.map(this.separator_, + function(c) { return c.charCodeAt(0); }); } // The version of encryption we implement @@ -310,8 +310,11 @@ PROT_UrlCrypto.prototype.updateMacFromString = function(s) { throw new Error ("Initialize mac first"); } - var arr = this.base64_.arrayifyString(s); - this.macer_.updateFromArray(arr); + var stream = Cc['@mozilla.org/io/string-input-stream;1'] + .createInstance(Ci.nsIStringInputStream); + stream.setData(s, s.length); + if (stream.available() > 0) + this.macer_.updateFromStream(stream); } /** @@ -373,11 +376,11 @@ PROT_UrlCrypto.prototype.computeMac = function(data, this.macer_.updateFromArray(clientKeyArray); this.macer_.updateFromArray(separatorArray); - // Note to self: calling G_CryptoHasher.updateFromString ain't the same as - // arrayifying the string and then calling updateFromArray. Not sure if - // that's a bug in G_CryptoHasher or not. Niels, what do you think? - var arr = this.base64_.arrayifyString(data); - this.macer_.updateFromArray(arr); + var stream = Cc['@mozilla.org/io/string-input-stream;1'] + .createInstance(Ci.nsIStringInputStream); + stream.setData(data, data.length); + if (stream.available() > 0) + this.macer_.updateFromStream(stream); this.macer_.updateFromArray(separatorArray); this.macer_.updateFromArray(clientKeyArray); @@ -449,7 +452,6 @@ function TEST_PROT_UrlCrypto() { "Output query params doesn't have: " + PROT_UrlCrypto.QPS[p]); // Now test that encryption is determinisitic - var b64 = new G_Base64(); // Some helper functions function arrayEquals(a1, a2) { @@ -479,6 +481,10 @@ function TEST_PROT_UrlCrypto() { var startCrypt = (new Date).getTime(); var numCrypts = 0; + // Helper function to arrayify string. + function toCharCode(c) { + return c.charCodeAt(0); + } // Set this to true for extended testing var doLongTest = false; if (doLongTest) { @@ -504,9 +510,9 @@ function TEST_PROT_UrlCrypto() { for (var payloadPadding = 0; payloadPadding < count; payloadPadding++) payload += "a"; - var plaintext1 = b64.arrayifyString(payload); - var plaintext2 = b64.arrayifyString(payload); - var plaintext3 = b64.arrayifyString(payload); + var plaintext1 = Array.map(payload, toCharCode); + var plaintext2 = Array.map(payload, toCharCode); + var plaintext3 = Array.map(payload, toCharCode); // Verify that encryption is deterministic given set parameters numCrypts++; @@ -528,14 +534,17 @@ function TEST_PROT_UrlCrypto() { numCrypts++; // Now verify that it is symmetrical + var b64arr = Array.map(atob(ciphertext2), toCharCode); var ciphertext3 = c.encryptV1(clientKeyArray, "1", count, - b64.decodeString(ciphertext2), + b64arr, true /* websafe */); - G_Assert(z, arrayEquals(plaintext3, b64.decodeString(ciphertext3, - true/*websafe*/)), + // note: ciphertext3 was websafe - reverting to plain base64 first + var b64str = atob(ciphertext3).replace(/-/g, "+").replace(/_/g, "/"); + b64arr = Array.map(b64str, toCharCode) + G_Assert(z, arrayEquals(plaintext3, b64arr), "Encryption and decryption not symmetrical"); } } @@ -593,7 +602,8 @@ function TEST_PROT_UrlCrypto() { ciphertexts[36]="Q0nZXFPJbpx1WZPP-lLPuSGR-pD08B4CAW-6Uf0eEkS05-oM"; ciphertexts[37]="XeKfieZGc9bPh7nRtCgujF8OY14zbIZSK20Lwg1HTpHi9HfXVQ=="; - var clientKeyArray = b64.decodeString("dtmbEN1kgN/LmuEoYifaFw=="); + var clientKey = "dtmbEN1kgN/LmuEoYifaFw=="; + var clientKeyArray = Array.map(atob(clientKey), toCharCode); // wrappedKey was "MTpPH3pnLDKihecOci+0W5dk" var count = 0xFEDCBA09; var plaintext = "http://www.foobar.com/this?is&some=url"; @@ -602,7 +612,7 @@ function TEST_PROT_UrlCrypto() { // that we get what we expect when we encrypt for (var i = 0; i < plaintext.length; i++) { - var plaintextArray = b64.arrayifyString(plaintext.substring(0, i)); + var plaintextArray = Array.map(plaintext.slice(0, i), toCharCode); var crypted = c.encryptV1(clientKeyArray, "1", count + i, diff --git a/toolkit/components/url-classifier/src/nsUrlClassifierLib.js b/toolkit/components/url-classifier/src/nsUrlClassifierLib.js index c32bd5d4337..5312855d206 100644 --- a/toolkit/components/url-classifier/src/nsUrlClassifierLib.js +++ b/toolkit/components/url-classifier/src/nsUrlClassifierLib.js @@ -47,7 +47,6 @@ const G_GDEBUG = false; #include ../content/moz/preferences.js #include ../content/moz/debug.js #include ../content/moz/alarm.js -#include ../content/moz/base64.js #include ../content/moz/cryptohasher.js #include ../content/moz/objectsafemap.js #include ../content/moz/observer.js diff --git a/toolkit/components/url-classifier/src/nsUrlClassifierTable.js b/toolkit/components/url-classifier/src/nsUrlClassifierTable.js index 84b022460c3..b536bd26404 100644 --- a/toolkit/components/url-classifier/src/nsUrlClassifierTable.js +++ b/toolkit/components/url-classifier/src/nsUrlClassifierTable.js @@ -53,7 +53,6 @@ function Init() { modScope.G_PreferenceObserver = jslib.G_PreferenceObserver; modScope.G_Debug = jslib.G_Debug; modScope.G_CryptoHasher = jslib.G_CryptoHasher; - modScope.G_Base64 = jslib.G_Base64; modScope.BindToObject = jslib.BindToObject; // We only need to call Init once.