diff options
Diffstat (limited to 'toolkit/components/url-classifier/content/moz/cryptohasher.js')
-rw-r--r-- | toolkit/components/url-classifier/content/moz/cryptohasher.js | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/content/moz/cryptohasher.js b/toolkit/components/url-classifier/content/moz/cryptohasher.js new file mode 100644 index 000000000..a1294aa93 --- /dev/null +++ b/toolkit/components/url-classifier/content/moz/cryptohasher.js @@ -0,0 +1,176 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +// A very thin wrapper around nsICryptoHash. It's not strictly +// necessary, but makes the code a bit cleaner and gives us the +// opportunity to verify that our implementations give the results that +// we expect, for example if we have to interoperate with a server. +// +// The digest* methods reset the state of the hasher, so it's +// necessary to call init() explicitly after them. +// +// Works only in Firefox 1.5+. +// +// IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024 +// you cannot use the cryptohasher before app-startup. The symptom of doing +// so is a segfault in NSS. + +/** + * Instantiate a new hasher. You must explicitly call init() before use! + */ +this.G_CryptoHasher = +function G_CryptoHasher() { + this.debugZone = "cryptohasher"; + this.hasher_ = null; +} + +G_CryptoHasher.algorithms = { + MD2: Ci.nsICryptoHash.MD2, + MD5: Ci.nsICryptoHash.MD5, + SHA1: Ci.nsICryptoHash.SHA1, + SHA256: Ci.nsICryptoHash.SHA256, + SHA384: Ci.nsICryptoHash.SHA384, + SHA512: Ci.nsICryptoHash.SHA512, +}; + +/** + * Initialize the hasher. This function must be called after every call + * to one of the digest* methods. + * + * @param algorithm Constant from G_CryptoHasher.algorithms specifying the + * algorithm this hasher will use + */ +G_CryptoHasher.prototype.init = function(algorithm) { + var validAlgorithm = false; + for (var alg in G_CryptoHasher.algorithms) + if (algorithm == G_CryptoHasher.algorithms[alg]) + validAlgorithm = true; + + if (!validAlgorithm) + throw new Error("Invalid algorithm: " + algorithm); + + this.hasher_ = Cc["@mozilla.org/security/hash;1"] + .createInstance(Ci.nsICryptoHash); + this.hasher_.init(algorithm); +} + +/** + * Update the hash's internal state with input given in a string. Can be + * called multiple times for incrementeal hash updates. + * + * @param input String containing data to hash. + */ +G_CryptoHasher.prototype.updateFromString = function(input) { + if (!this.hasher_) + throw new Error("You must initialize the hasher first!"); + + var stream = Cc['@mozilla.org/io/string-input-stream;1'] + .createInstance(Ci.nsIStringInputStream); + stream.setData(input, input.length); + this.updateFromStream(stream); +} + +/** + * Update the hash's internal state with input given in an array. Can be + * called multiple times for incremental hash updates. + * + * @param input Array containing data to hash. + */ +G_CryptoHasher.prototype.updateFromArray = function(input) { + if (!this.hasher_) + throw new Error("You must initialize the hasher first!"); + + this.hasher_.update(input, input.length); +} + +/** + * Update the hash's internal state with input given in a stream. Can be + * called multiple times from incremental hash updates. + */ +G_CryptoHasher.prototype.updateFromStream = function(stream) { + if (!this.hasher_) + throw new Error("You must initialize the hasher first!"); + + if (stream.available()) + this.hasher_.updateFromStream(stream, stream.available()); +} + +/** + * @returns The hash value as a string (sequence of 8-bit values) + */ +G_CryptoHasher.prototype.digestRaw = function() { + var digest = this.hasher_.finish(false /* not b64 encoded */); + this.hasher_ = null; + return digest; +} + +/** + * @returns The hash value as a base64-encoded string + */ +G_CryptoHasher.prototype.digestBase64 = function() { + var digest = this.hasher_.finish(true /* b64 encoded */); + this.hasher_ = null; + return digest; +} + +/** + * @returns The hash value as a hex-encoded string + */ +G_CryptoHasher.prototype.digestHex = function() { + var raw = this.digestRaw(); + return this.toHex_(raw); +} + +/** + * Converts a sequence of values to a hex-encoded string. The input is a + * a string, so you can stick 16-bit values in each character. + * + * @param str String to conver to hex. (Often this is just a sequence of + * 16-bit values) + * + * @returns String containing the hex representation of the input + */ +G_CryptoHasher.prototype.toHex_ = function(str) { + var hexchars = '0123456789ABCDEF'; + var hexrep = new Array(str.length * 2); + + for (var i = 0; i < str.length; ++i) { + hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15); + hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15); + } + return hexrep.join(''); +} + +#ifdef DEBUG +/** + * Lame unittest function + */ +this.TEST_G_CryptoHasher = function TEST_G_CryptoHasher() { + if (G_GDEBUG) { + var z = "cryptohasher UNITTEST"; + G_debugService.enableZone(z); + + G_Debug(z, "Starting"); + + var md5 = function(str) { + var hasher = new G_CryptoHasher(); + hasher.init(G_CryptoHasher.algorithms.MD5); + hasher.updateFromString(str); + return hasher.digestHex().toLowerCase(); + }; + + // test vectors from: http://www.faqs.org/rfcs/rfc1321.html + var vectors = {"": "d41d8cd98f00b204e9800998ecf8427e", + "a": "0cc175b9c0f1b6a831c399e269772661", + "abc": "900150983cd24fb0d6963f7d28e17f72", + "message digest": "f96b697d7cb7938d525a2f31aaf161d0", + "abcdefghijklmnopqrstuvwxyz": "c3fcd3d76192e4007dfb496cca67e13b", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789": "d174ab98d277d9f5a5611c2c9f419d9f", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890": "57edf4a22be3c955ac49da2e2107b67a"}; + + G_Debug(z, "PASSED"); + } +} +#endif |